mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
Merge pull request #664 from lightpanda-io/treewalker
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / puppeteer-perf (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / puppeteer-perf (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
Add TreeWalker
This commit is contained in:
@@ -30,6 +30,9 @@ const css = @import("css.zig");
|
|||||||
|
|
||||||
const Element = @import("element.zig").Element;
|
const Element = @import("element.zig").Element;
|
||||||
const ElementUnion = @import("element.zig").Union;
|
const ElementUnion = @import("element.zig").Union;
|
||||||
|
const TreeWalker = @import("tree_walker.zig").TreeWalker;
|
||||||
|
|
||||||
|
const Env = @import("../env.zig").Env;
|
||||||
|
|
||||||
const DOMImplementation = @import("implementation.zig").DOMImplementation;
|
const DOMImplementation = @import("implementation.zig").DOMImplementation;
|
||||||
|
|
||||||
@@ -238,6 +241,10 @@ pub const Document = struct {
|
|||||||
pub fn _replaceChildren(self: *parser.Document, nodes: []const Node.NodeOrText) !void {
|
pub fn _replaceChildren(self: *parser.Document, nodes: []const Node.NodeOrText) !void {
|
||||||
return Node.replaceChildren(parser.documentToNode(self), nodes);
|
return Node.replaceChildren(parser.documentToNode(self), nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn _createTreeWalker(_: *parser.Document, root: *parser.Node, what_to_show: ?u32, filter: ?TreeWalker.TreeWalkerOpts) !TreeWalker {
|
||||||
|
return try TreeWalker.init(root, what_to_show, filter);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const testing = @import("../../testing.zig");
|
const testing = @import("../../testing.zig");
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ const Node = @import("node.zig");
|
|||||||
const MutationObserver = @import("mutation_observer.zig");
|
const MutationObserver = @import("mutation_observer.zig");
|
||||||
const IntersectionObserver = @import("intersection_observer.zig");
|
const IntersectionObserver = @import("intersection_observer.zig");
|
||||||
const DOMParser = @import("dom_parser.zig").DOMParser;
|
const DOMParser = @import("dom_parser.zig").DOMParser;
|
||||||
|
const TreeWalker = @import("tree_walker.zig").TreeWalker;
|
||||||
|
const NodeFilter = @import("node_filter.zig").NodeFilter;
|
||||||
|
|
||||||
pub const Interfaces = .{
|
pub const Interfaces = .{
|
||||||
DOMException,
|
DOMException,
|
||||||
@@ -39,4 +41,6 @@ pub const Interfaces = .{
|
|||||||
MutationObserver.Interfaces,
|
MutationObserver.Interfaces,
|
||||||
IntersectionObserver.Interfaces,
|
IntersectionObserver.Interfaces,
|
||||||
DOMParser,
|
DOMParser,
|
||||||
|
TreeWalker,
|
||||||
|
NodeFilter,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ pub const EventTarget = struct {
|
|||||||
pub fn _addEventListener(
|
pub fn _addEventListener(
|
||||||
self: *parser.EventTarget,
|
self: *parser.EventTarget,
|
||||||
typ: []const u8,
|
typ: []const u8,
|
||||||
cbk: Env.Callback,
|
cbk: Env.Function,
|
||||||
opts_: ?AddEventListenerOpts,
|
opts_: ?AddEventListenerOpts,
|
||||||
state: *SessionState,
|
state: *SessionState,
|
||||||
) !void {
|
) !void {
|
||||||
@@ -104,7 +104,7 @@ pub const EventTarget = struct {
|
|||||||
pub fn _removeEventListener(
|
pub fn _removeEventListener(
|
||||||
self: *parser.EventTarget,
|
self: *parser.EventTarget,
|
||||||
typ: []const u8,
|
typ: []const u8,
|
||||||
cbk: Env.Callback,
|
cbk: Env.Function,
|
||||||
capture: ?bool,
|
capture: ?bool,
|
||||||
// TODO: hanle EventListenerOptions
|
// TODO: hanle EventListenerOptions
|
||||||
// see #https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
// see #https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const log = std.log.scoped(.events);
|
|||||||
// The returned Entries are phony, they always indicate full intersection.
|
// The returned Entries are phony, they always indicate full intersection.
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver
|
// https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver
|
||||||
pub const IntersectionObserver = struct {
|
pub const IntersectionObserver = struct {
|
||||||
callback: Env.Callback,
|
callback: Env.Function,
|
||||||
options: IntersectionObserverOptions,
|
options: IntersectionObserverOptions,
|
||||||
state: *SessionState,
|
state: *SessionState,
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ pub const IntersectionObserver = struct {
|
|||||||
|
|
||||||
// new IntersectionObserver(callback)
|
// new IntersectionObserver(callback)
|
||||||
// new IntersectionObserver(callback, options) [not supported yet]
|
// new IntersectionObserver(callback, options) [not supported yet]
|
||||||
pub fn constructor(callback: Env.Callback, options_: ?IntersectionObserverOptions, state: *SessionState) !IntersectionObserver {
|
pub fn constructor(callback: Env.Function, options_: ?IntersectionObserverOptions, state: *SessionState) !IntersectionObserver {
|
||||||
var options = IntersectionObserverOptions{
|
var options = IntersectionObserverOptions{
|
||||||
.root = parser.documentToNode(parser.documentHTMLToDocument(state.window.document)),
|
.root = parser.documentToNode(parser.documentHTMLToDocument(state.window.document)),
|
||||||
.rootMargin = "0px 0px 0px 0px",
|
.rootMargin = "0px 0px 0px 0px",
|
||||||
@@ -85,8 +85,8 @@ pub const IntersectionObserver = struct {
|
|||||||
.options = &self.options,
|
.options = &self.options,
|
||||||
});
|
});
|
||||||
|
|
||||||
var result: Env.Callback.Result = undefined;
|
var result: Env.Function.Result = undefined;
|
||||||
self.callback.tryCall(.{self.observed_entries.items}, &result) catch {
|
self.callback.tryCall(void, .{self.observed_entries.items}, &result) catch {
|
||||||
log.err("intersection observer callback error: {s}", .{result.exception});
|
log.err("intersection observer callback error: {s}", .{result.exception});
|
||||||
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,14 +36,14 @@ const log = std.log.scoped(.events);
|
|||||||
|
|
||||||
// WEB IDL https://dom.spec.whatwg.org/#interface-mutationobserver
|
// WEB IDL https://dom.spec.whatwg.org/#interface-mutationobserver
|
||||||
pub const MutationObserver = struct {
|
pub const MutationObserver = struct {
|
||||||
cbk: Env.Callback,
|
cbk: Env.Function,
|
||||||
arena: Allocator,
|
arena: Allocator,
|
||||||
|
|
||||||
// List of records which were observed. When the scopeEnds, we need to
|
// List of records which were observed. When the scopeEnds, we need to
|
||||||
// execute our callback with it.
|
// execute our callback with it.
|
||||||
observed: std.ArrayListUnmanaged(*MutationRecord),
|
observed: std.ArrayListUnmanaged(*MutationRecord),
|
||||||
|
|
||||||
pub fn constructor(cbk: Env.Callback, state: *SessionState) !MutationObserver {
|
pub fn constructor(cbk: Env.Function, state: *SessionState) !MutationObserver {
|
||||||
return .{
|
return .{
|
||||||
.cbk = cbk,
|
.cbk = cbk,
|
||||||
.observed = .{},
|
.observed = .{},
|
||||||
@@ -113,8 +113,8 @@ pub const MutationObserver = struct {
|
|||||||
|
|
||||||
for (record) |r| {
|
for (record) |r| {
|
||||||
const records = [_]MutationRecord{r.*};
|
const records = [_]MutationRecord{r.*};
|
||||||
var result: Env.Callback.Result = undefined;
|
var result: Env.Function.Result = undefined;
|
||||||
self.cbk.tryCall(.{records}, &result) catch {
|
self.cbk.tryCall(void, .{records}, &result) catch {
|
||||||
log.err("mutation observer callback error: {s}", .{result.exception});
|
log.err("mutation observer callback error: {s}", .{result.exception});
|
||||||
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
||||||
};
|
};
|
||||||
|
|||||||
52
src/browser/dom/node_filter.zig
Normal file
52
src/browser/dom/node_filter.zig
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
|
||||||
|
//
|
||||||
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const NodeFilter = struct {
|
||||||
|
pub const _FILTER_ACCEPT: u16 = 1;
|
||||||
|
pub const _FILTER_REJECT: u16 = 2;
|
||||||
|
pub const _FILTER_SKIP: u16 = 3;
|
||||||
|
pub const _SHOW_ALL: u32 = std.math.maxInt(u32);
|
||||||
|
pub const _SHOW_ELEMENT: u32 = 0b1;
|
||||||
|
pub const _SHOW_ATTRIBUTE: u32 = 0b10;
|
||||||
|
pub const _SHOW_TEXT: u32 = 0b100;
|
||||||
|
pub const _SHOW_CDATA_SECTION: u32 = 0b1000;
|
||||||
|
pub const _SHOW_ENTITY_REFERENCE: u32 = 0b10000;
|
||||||
|
pub const _SHOW_ENTITY: u32 = 0b100000;
|
||||||
|
pub const _SHOW_PROCESSING_INSTRUCTION: u32 = 0b1000000;
|
||||||
|
pub const _SHOW_COMMENT: u32 = 0b10000000;
|
||||||
|
pub const _SHOW_DOCUMENT: u32 = 0b100000000;
|
||||||
|
pub const _SHOW_DOCUMENT_TYPE: u32 = 0b1000000000;
|
||||||
|
pub const _SHOW_DOCUMENT_FRAGMENT: u32 = 0b10000000000;
|
||||||
|
pub const _SHOW_NOTATION: u32 = 0b100000000000;
|
||||||
|
};
|
||||||
|
|
||||||
|
const testing = @import("../../testing.zig");
|
||||||
|
test "Browser.DOM.NodeFilter" {
|
||||||
|
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
|
||||||
|
defer runner.deinit();
|
||||||
|
|
||||||
|
try runner.testCases(&.{
|
||||||
|
.{ "NodeFilter.FILTER_ACCEPT", "1" },
|
||||||
|
.{ "NodeFilter.FILTER_REJECT", "2" },
|
||||||
|
.{ "NodeFilter.FILTER_SKIP", "3" },
|
||||||
|
.{ "NodeFilter.SHOW_ALL", "4294967295" },
|
||||||
|
.{ "NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT", "129" },
|
||||||
|
}, .{});
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ const std = @import("std");
|
|||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
|
|
||||||
const JsThis = @import("../env.zig").JsThis;
|
const JsThis = @import("../env.zig").JsThis;
|
||||||
const Callback = @import("../env.zig").Callback;
|
const Function = @import("../env.zig").Function;
|
||||||
|
|
||||||
const NodeUnion = @import("node.zig").Union;
|
const NodeUnion = @import("node.zig").Union;
|
||||||
const Node = @import("node.zig").Node;
|
const Node = @import("node.zig").Node;
|
||||||
@@ -141,11 +141,11 @@ pub const NodeList = struct {
|
|||||||
// };
|
// };
|
||||||
// }
|
// }
|
||||||
|
|
||||||
pub fn _forEach(self: *NodeList, cbk: Callback) !void { // TODO handle thisArg
|
pub fn _forEach(self: *NodeList, cbk: Function) !void { // TODO handle thisArg
|
||||||
for (self.nodes.items, 0..) |n, i| {
|
for (self.nodes.items, 0..) |n, i| {
|
||||||
const ii: u32 = @intCast(i);
|
const ii: u32 = @intCast(i);
|
||||||
var result: Callback.Result = undefined;
|
var result: Function.Result = undefined;
|
||||||
cbk.tryCall(.{ n, ii, self }, &result) catch {
|
cbk.tryCall(void, .{ n, ii, self }, &result) catch {
|
||||||
log.err("callback error: {s}", .{result.exception});
|
log.err("callback error: {s}", .{result.exception});
|
||||||
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const std = @import("std");
|
|||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const iterator = @import("../iterator/iterator.zig");
|
const iterator = @import("../iterator/iterator.zig");
|
||||||
|
|
||||||
const Callback = @import("../env.zig").Callback;
|
const Function = @import("../env.zig").Function;
|
||||||
const JsObject = @import("../env.zig").JsObject;
|
const JsObject = @import("../env.zig").JsObject;
|
||||||
const DOMException = @import("exceptions.zig").DOMException;
|
const DOMException = @import("exceptions.zig").DOMException;
|
||||||
|
|
||||||
@@ -138,11 +138,11 @@ pub const DOMTokenList = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO handle thisArg
|
// TODO handle thisArg
|
||||||
pub fn _forEach(self: *parser.TokenList, cbk: Callback, this_arg: JsObject) !void {
|
pub fn _forEach(self: *parser.TokenList, cbk: Function, this_arg: JsObject) !void {
|
||||||
var entries = _entries(self);
|
var entries = _entries(self);
|
||||||
while (try entries._next()) |entry| {
|
while (try entries._next()) |entry| {
|
||||||
var result: Callback.Result = undefined;
|
var result: Function.Result = undefined;
|
||||||
cbk.tryCallWithThis(this_arg, .{ entry.@"1", entry.@"0", self }, &result) catch {
|
cbk.tryCallWithThis(void, this_arg, .{ entry.@"1", entry.@"0", self }, &result) catch {
|
||||||
log.err("callback error: {s}", .{result.exception});
|
log.err("callback error: {s}", .{result.exception});
|
||||||
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
||||||
};
|
};
|
||||||
|
|||||||
293
src/browser/dom/tree_walker.zig
Normal file
293
src/browser/dom/tree_walker.zig
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
|
||||||
|
//
|
||||||
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const parser = @import("../netsurf.zig");
|
||||||
|
|
||||||
|
const NodeFilter = @import("node_filter.zig").NodeFilter;
|
||||||
|
const Env = @import("../env.zig").Env;
|
||||||
|
const SessionState = @import("../env.zig").SessionState;
|
||||||
|
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker
|
||||||
|
pub const TreeWalker = struct {
|
||||||
|
root: *parser.Node,
|
||||||
|
current_node: *parser.Node,
|
||||||
|
what_to_show: u32,
|
||||||
|
filter: ?Env.Function,
|
||||||
|
|
||||||
|
pub const TreeWalkerOpts = union(enum) {
|
||||||
|
function: Env.Function,
|
||||||
|
object: struct { acceptNode: Env.Function },
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init(node: *parser.Node, what_to_show: ?u32, filter: ?TreeWalkerOpts) !TreeWalker {
|
||||||
|
var filter_func: ?Env.Function = null;
|
||||||
|
|
||||||
|
if (filter) |f| {
|
||||||
|
filter_func = switch (f) {
|
||||||
|
.function => |func| func,
|
||||||
|
.object => |o| o.acceptNode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.root = node,
|
||||||
|
.current_node = node,
|
||||||
|
.what_to_show = what_to_show orelse NodeFilter._SHOW_ALL,
|
||||||
|
.filter = filter_func,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const VerifyResult = enum { accept, skip, reject };
|
||||||
|
|
||||||
|
fn verify(self: *const TreeWalker, node: *parser.Node) !VerifyResult {
|
||||||
|
const node_type = try parser.nodeType(node);
|
||||||
|
const what_to_show = self.what_to_show;
|
||||||
|
|
||||||
|
// Verify that we can show this node type.
|
||||||
|
if (!switch (node_type) {
|
||||||
|
.attribute => what_to_show & NodeFilter._SHOW_ATTRIBUTE != 0,
|
||||||
|
.cdata_section => what_to_show & NodeFilter._SHOW_CDATA_SECTION != 0,
|
||||||
|
.comment => what_to_show & NodeFilter._SHOW_COMMENT != 0,
|
||||||
|
.document => what_to_show & NodeFilter._SHOW_DOCUMENT != 0,
|
||||||
|
.document_fragment => what_to_show & NodeFilter._SHOW_DOCUMENT_FRAGMENT != 0,
|
||||||
|
.document_type => what_to_show & NodeFilter._SHOW_DOCUMENT_TYPE != 0,
|
||||||
|
.element => what_to_show & NodeFilter._SHOW_ELEMENT != 0,
|
||||||
|
.entity => what_to_show & NodeFilter._SHOW_ENTITY != 0,
|
||||||
|
.entity_reference => what_to_show & NodeFilter._SHOW_ENTITY_REFERENCE != 0,
|
||||||
|
.notation => what_to_show & NodeFilter._SHOW_NOTATION != 0,
|
||||||
|
.processing_instruction => what_to_show & NodeFilter._SHOW_PROCESSING_INSTRUCTION != 0,
|
||||||
|
.text => what_to_show & NodeFilter._SHOW_TEXT != 0,
|
||||||
|
}) return .reject;
|
||||||
|
|
||||||
|
// Verify that we aren't filtering it out.
|
||||||
|
if (self.filter) |f| {
|
||||||
|
const filter = try f.call(u32, .{node});
|
||||||
|
return switch (filter) {
|
||||||
|
NodeFilter._FILTER_ACCEPT => .accept,
|
||||||
|
NodeFilter._FILTER_REJECT => .reject,
|
||||||
|
NodeFilter._FILTER_SKIP => .skip,
|
||||||
|
else => .reject,
|
||||||
|
};
|
||||||
|
} else return .accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_root(self: *TreeWalker) *parser.Node {
|
||||||
|
return self.root;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_currentNode(self: *TreeWalker) *parser.Node {
|
||||||
|
return self.current_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_whatToShow(self: *TreeWalker) u32 {
|
||||||
|
return self.what_to_show;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_filter(self: *TreeWalker) ?Env.Function {
|
||||||
|
return self.filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_currentNode(self: *TreeWalker, node: *parser.Node) !void {
|
||||||
|
self.current_node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn firstChild(self: *const TreeWalker, node: *parser.Node) !?*parser.Node {
|
||||||
|
const children = try parser.nodeGetChildNodes(node);
|
||||||
|
const child_count = try parser.nodeListLength(children);
|
||||||
|
|
||||||
|
for (0..child_count) |i| {
|
||||||
|
const index: u32 = @intCast(i);
|
||||||
|
const child = (try parser.nodeListItem(children, index)) orelse return null;
|
||||||
|
|
||||||
|
switch (try self.verify(child)) {
|
||||||
|
.accept => return child,
|
||||||
|
.reject => continue,
|
||||||
|
.skip => if (try self.firstChild(child)) |gchild| return gchild,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lastChild(self: *const TreeWalker, node: *parser.Node) !?*parser.Node {
|
||||||
|
const children = try parser.nodeGetChildNodes(node);
|
||||||
|
const child_count = try parser.nodeListLength(children);
|
||||||
|
|
||||||
|
var index: u32 = child_count;
|
||||||
|
while (index > 0) {
|
||||||
|
index -= 1;
|
||||||
|
const child = (try parser.nodeListItem(children, index)) orelse return null;
|
||||||
|
|
||||||
|
switch (try self.verify(child)) {
|
||||||
|
.accept => return child,
|
||||||
|
.reject => continue,
|
||||||
|
.skip => if (try self.lastChild(child)) |gchild| return gchild,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nextSibling(self: *const TreeWalker, node: *parser.Node) !?*parser.Node {
|
||||||
|
var current = node;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
current = (try parser.nodeNextSibling(current)) orelse return null;
|
||||||
|
|
||||||
|
switch (try self.verify(current)) {
|
||||||
|
.accept => return current,
|
||||||
|
.skip, .reject => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn previousSibling(self: *const TreeWalker, node: *parser.Node) !?*parser.Node {
|
||||||
|
var current = node;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
current = (try parser.nodePreviousSibling(current)) orelse return null;
|
||||||
|
|
||||||
|
switch (try self.verify(current)) {
|
||||||
|
.accept => return current,
|
||||||
|
.skip, .reject => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parentNode(self: *const TreeWalker, node: *parser.Node) !?*parser.Node {
|
||||||
|
if (self.root == node) return null;
|
||||||
|
|
||||||
|
var current = node;
|
||||||
|
while (true) {
|
||||||
|
if (current == self.root) return null;
|
||||||
|
current = (try parser.nodeParentNode(current)) orelse return null;
|
||||||
|
|
||||||
|
switch (try self.verify(current)) {
|
||||||
|
.accept => return current,
|
||||||
|
.reject, .skip => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _firstChild(self: *TreeWalker) !?*parser.Node {
|
||||||
|
if (try self.firstChild(self.current_node)) |child| {
|
||||||
|
self.current_node = child;
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _lastChild(self: *TreeWalker) !?*parser.Node {
|
||||||
|
if (try self.lastChild(self.current_node)) |child| {
|
||||||
|
self.current_node = child;
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _nextNode(self: *TreeWalker) !?*parser.Node {
|
||||||
|
if (try self.firstChild(self.current_node)) |child| {
|
||||||
|
self.current_node = child;
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
var current = self.current_node;
|
||||||
|
while (current != self.root) {
|
||||||
|
if (try self.nextSibling(current)) |sibling| {
|
||||||
|
self.current_node = sibling;
|
||||||
|
return sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = (try parser.nodeParentNode(current)) orelse break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _nextSibling(self: *TreeWalker) !?*parser.Node {
|
||||||
|
if (try self.nextSibling(self.current_node)) |sibling| {
|
||||||
|
self.current_node = sibling;
|
||||||
|
return sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _parentNode(self: *TreeWalker) !?*parser.Node {
|
||||||
|
if (try self.parentNode(self.current_node)) |parent| {
|
||||||
|
self.current_node = parent;
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _previousNode(self: *TreeWalker) !?*parser.Node {
|
||||||
|
var current = self.current_node;
|
||||||
|
while (try parser.nodePreviousSibling(current)) |previous| {
|
||||||
|
current = previous;
|
||||||
|
|
||||||
|
switch (try self.verify(current)) {
|
||||||
|
.accept => {
|
||||||
|
// Get last child if it has one.
|
||||||
|
if (try self.lastChild(current)) |child| {
|
||||||
|
self.current_node = child;
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, this node is our previous one.
|
||||||
|
self.current_node = current;
|
||||||
|
return current;
|
||||||
|
},
|
||||||
|
.reject => continue,
|
||||||
|
.skip => {
|
||||||
|
// Get last child if it has one.
|
||||||
|
if (try self.lastChild(current)) |child| {
|
||||||
|
self.current_node = child;
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current != self.root) {
|
||||||
|
if (try self.parentNode(current)) |parent| {
|
||||||
|
self.current_node = parent;
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _previousSibling(self: *TreeWalker) !?*parser.Node {
|
||||||
|
if (try self.previousSibling(self.current_node)) |sibling| {
|
||||||
|
self.current_node = sibling;
|
||||||
|
return sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -43,7 +43,7 @@ const WebApis = struct {
|
|||||||
|
|
||||||
pub const JsThis = Env.JsThis;
|
pub const JsThis = Env.JsThis;
|
||||||
pub const JsObject = Env.JsObject;
|
pub const JsObject = Env.JsObject;
|
||||||
pub const Callback = Env.Callback;
|
pub const Function = Env.Function;
|
||||||
pub const Env = js.Env(*SessionState, WebApis);
|
pub const Env = js.Env(*SessionState, WebApis);
|
||||||
|
|
||||||
const Window = @import("html/window.zig").Window;
|
const Window = @import("html/window.zig").Window;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const std = @import("std");
|
|||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const Callback = @import("../env.zig").Callback;
|
const Function = @import("../env.zig").Function;
|
||||||
const generate = @import("../../runtime/generate.zig");
|
const generate = @import("../../runtime/generate.zig");
|
||||||
|
|
||||||
const DOMException = @import("../dom/exceptions.zig").DOMException;
|
const DOMException = @import("../dom/exceptions.zig").DOMException;
|
||||||
@@ -140,10 +140,10 @@ pub const Event = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const EventHandler = struct {
|
pub const EventHandler = struct {
|
||||||
callback: Callback,
|
callback: Function,
|
||||||
node: parser.EventNode,
|
node: parser.EventNode,
|
||||||
|
|
||||||
pub fn init(allocator: Allocator, callback: Callback) !*EventHandler {
|
pub fn init(allocator: Allocator, callback: Function) !*EventHandler {
|
||||||
const eh = try allocator.create(EventHandler);
|
const eh = try allocator.create(EventHandler);
|
||||||
eh.* = .{
|
eh.* = .{
|
||||||
.callback = callback,
|
.callback = callback,
|
||||||
@@ -162,8 +162,8 @@ pub const EventHandler = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const self: *EventHandler = @fieldParentPtr("node", node);
|
const self: *EventHandler = @fieldParentPtr("node", node);
|
||||||
var result: Callback.Result = undefined;
|
var result: Function.Result = undefined;
|
||||||
self.callback.tryCall(.{ievent}, &result) catch {
|
self.callback.tryCall(void, .{ievent}, &result) catch {
|
||||||
log.err("event handler error: {s}", .{result.exception});
|
log.err("event handler error: {s}", .{result.exception});
|
||||||
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const Callback = @import("../env.zig").Callback;
|
const Function = @import("../env.zig").Function;
|
||||||
const EventTarget = @import("../dom/event_target.zig").EventTarget;
|
const EventTarget = @import("../dom/event_target.zig").EventTarget;
|
||||||
|
|
||||||
// https://drafts.csswg.org/cssom-view/#the-mediaquerylist-interface
|
// https://drafts.csswg.org/cssom-view/#the-mediaquerylist-interface
|
||||||
@@ -39,7 +39,7 @@ pub const MediaQueryList = struct {
|
|||||||
return self.media;
|
return self.media;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _addListener(_: *const MediaQueryList, _: Callback) void {}
|
pub fn _addListener(_: *const MediaQueryList, _: Function) void {}
|
||||||
|
|
||||||
pub fn _removeListener(_: *const MediaQueryList, _: Callback) void {}
|
pub fn _removeListener(_: *const MediaQueryList, _: Function) void {}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const Callback = @import("../env.zig").Callback;
|
const Function = @import("../env.zig").Function;
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const SessionState = @import("../env.zig").SessionState;
|
||||||
const Loop = @import("../../runtime/loop.zig").Loop;
|
const Loop = @import("../../runtime/loop.zig").Loop;
|
||||||
|
|
||||||
@@ -159,12 +159,12 @@ pub const Window = struct {
|
|||||||
// Returns the request ID, that uniquely identifies the entry in the callback list.
|
// Returns the request ID, that uniquely identifies the entry in the callback list.
|
||||||
pub fn _requestAnimationFrame(
|
pub fn _requestAnimationFrame(
|
||||||
self: *Window,
|
self: *Window,
|
||||||
callback: Callback,
|
callback: Function,
|
||||||
) !u32 {
|
) !u32 {
|
||||||
// We immediately execute the callback, but this may not be correct TBD.
|
// We immediately execute the callback, but this may not be correct TBD.
|
||||||
// Since: When multiple callbacks queued by requestAnimationFrame() begin to fire in a single frame, each receives the same timestamp even though time has passed during the computation of every previous callback's workload.
|
// Since: When multiple callbacks queued by requestAnimationFrame() begin to fire in a single frame, each receives the same timestamp even though time has passed during the computation of every previous callback's workload.
|
||||||
var result: Callback.Result = undefined;
|
var result: Function.Result = undefined;
|
||||||
callback.tryCall(.{self.performance._now()}, &result) catch {
|
callback.tryCall(void, .{self.performance._now()}, &result) catch {
|
||||||
log.err("Window.requestAnimationFrame(): {s}", .{result.exception});
|
log.err("Window.requestAnimationFrame(): {s}", .{result.exception});
|
||||||
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
||||||
};
|
};
|
||||||
@@ -178,12 +178,12 @@ pub const Window = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO handle callback arguments.
|
// TODO handle callback arguments.
|
||||||
pub fn _setTimeout(self: *Window, cbk: Callback, delay: ?u32, state: *SessionState) !u32 {
|
pub fn _setTimeout(self: *Window, cbk: Function, delay: ?u32, state: *SessionState) !u32 {
|
||||||
return self.createTimeout(cbk, delay, state, false);
|
return self.createTimeout(cbk, delay, state, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO handle callback arguments.
|
// TODO handle callback arguments.
|
||||||
pub fn _setInterval(self: *Window, cbk: Callback, delay: ?u32, state: *SessionState) !u32 {
|
pub fn _setInterval(self: *Window, cbk: Function, delay: ?u32, state: *SessionState) !u32 {
|
||||||
return self.createTimeout(cbk, delay, state, true);
|
return self.createTimeout(cbk, delay, state, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@ pub const Window = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn createTimeout(self: *Window, cbk: Callback, delay_: ?u32, state: *SessionState, comptime repeat: bool) !u32 {
|
fn createTimeout(self: *Window, cbk: Function, delay_: ?u32, state: *SessionState, comptime repeat: bool) !u32 {
|
||||||
if (self.timers.count() > 512) {
|
if (self.timers.count() > 512) {
|
||||||
return error.TooManyTimeout;
|
return error.TooManyTimeout;
|
||||||
}
|
}
|
||||||
@@ -255,7 +255,7 @@ const TimerCallback = struct {
|
|||||||
timer_id: u31,
|
timer_id: u31,
|
||||||
|
|
||||||
// The JavaScript callback to execute
|
// The JavaScript callback to execute
|
||||||
cbk: Callback,
|
cbk: Function,
|
||||||
|
|
||||||
// This is the internal data that the event loop tracks. We'll get this
|
// This is the internal data that the event loop tracks. We'll get this
|
||||||
// back in run and, from it, can get our TimerCallback instance
|
// back in run and, from it, can get our TimerCallback instance
|
||||||
@@ -269,8 +269,8 @@ const TimerCallback = struct {
|
|||||||
fn run(node: *Loop.CallbackNode, repeat_delay: *?u63) void {
|
fn run(node: *Loop.CallbackNode, repeat_delay: *?u63) void {
|
||||||
const self: *TimerCallback = @fieldParentPtr("node", node);
|
const self: *TimerCallback = @fieldParentPtr("node", node);
|
||||||
|
|
||||||
var result: Callback.Result = undefined;
|
var result: Function.Result = undefined;
|
||||||
self.cbk.tryCall(.{}, &result) catch {
|
self.cbk.tryCall(void, .{}, &result) catch {
|
||||||
log.err("timeout callback error: {s}", .{result.exception});
|
log.err("timeout callback error: {s}", .{result.exception});
|
||||||
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
log.debug("stack:\n{s}", .{result.stack orelse "???"});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const Env = @import("../env.zig").Env;
|
const Env = @import("../env.zig").Env;
|
||||||
const Callback = Env.Callback;
|
const Function = Env.Function;
|
||||||
|
|
||||||
const EventTarget = @import("../dom/event_target.zig").EventTarget;
|
const EventTarget = @import("../dom/event_target.zig").EventTarget;
|
||||||
const EventHandler = @import("../events/event.zig").EventHandler;
|
const EventHandler = @import("../events/event.zig").EventHandler;
|
||||||
@@ -35,18 +35,18 @@ pub const XMLHttpRequestEventTarget = struct {
|
|||||||
// Extend libdom event target for pure zig struct.
|
// Extend libdom event target for pure zig struct.
|
||||||
base: parser.EventTargetTBase = parser.EventTargetTBase{},
|
base: parser.EventTargetTBase = parser.EventTargetTBase{},
|
||||||
|
|
||||||
onloadstart_cbk: ?Callback = null,
|
onloadstart_cbk: ?Function = null,
|
||||||
onprogress_cbk: ?Callback = null,
|
onprogress_cbk: ?Function = null,
|
||||||
onabort_cbk: ?Callback = null,
|
onabort_cbk: ?Function = null,
|
||||||
onload_cbk: ?Callback = null,
|
onload_cbk: ?Function = null,
|
||||||
ontimeout_cbk: ?Callback = null,
|
ontimeout_cbk: ?Function = null,
|
||||||
onloadend_cbk: ?Callback = null,
|
onloadend_cbk: ?Function = null,
|
||||||
|
|
||||||
fn register(
|
fn register(
|
||||||
self: *XMLHttpRequestEventTarget,
|
self: *XMLHttpRequestEventTarget,
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
typ: []const u8,
|
typ: []const u8,
|
||||||
cbk: Callback,
|
cbk: Function,
|
||||||
) !void {
|
) !void {
|
||||||
const target = @as(*parser.EventTarget, @ptrCast(self));
|
const target = @as(*parser.EventTarget, @ptrCast(self));
|
||||||
const eh = try EventHandler.init(alloc, try cbk.withThis(target));
|
const eh = try EventHandler.init(alloc, try cbk.withThis(target));
|
||||||
@@ -69,51 +69,51 @@ pub const XMLHttpRequestEventTarget = struct {
|
|||||||
try parser.eventTargetRemoveEventListener(et, typ, lst.?, false);
|
try parser.eventTargetRemoveEventListener(et, typ, lst.?, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_onloadstart(self: *XMLHttpRequestEventTarget) ?Callback {
|
pub fn get_onloadstart(self: *XMLHttpRequestEventTarget) ?Function {
|
||||||
return self.onloadstart_cbk;
|
return self.onloadstart_cbk;
|
||||||
}
|
}
|
||||||
pub fn get_onprogress(self: *XMLHttpRequestEventTarget) ?Callback {
|
pub fn get_onprogress(self: *XMLHttpRequestEventTarget) ?Function {
|
||||||
return self.onprogress_cbk;
|
return self.onprogress_cbk;
|
||||||
}
|
}
|
||||||
pub fn get_onabort(self: *XMLHttpRequestEventTarget) ?Callback {
|
pub fn get_onabort(self: *XMLHttpRequestEventTarget) ?Function {
|
||||||
return self.onabort_cbk;
|
return self.onabort_cbk;
|
||||||
}
|
}
|
||||||
pub fn get_onload(self: *XMLHttpRequestEventTarget) ?Callback {
|
pub fn get_onload(self: *XMLHttpRequestEventTarget) ?Function {
|
||||||
return self.onload_cbk;
|
return self.onload_cbk;
|
||||||
}
|
}
|
||||||
pub fn get_ontimeout(self: *XMLHttpRequestEventTarget) ?Callback {
|
pub fn get_ontimeout(self: *XMLHttpRequestEventTarget) ?Function {
|
||||||
return self.ontimeout_cbk;
|
return self.ontimeout_cbk;
|
||||||
}
|
}
|
||||||
pub fn get_onloadend(self: *XMLHttpRequestEventTarget) ?Callback {
|
pub fn get_onloadend(self: *XMLHttpRequestEventTarget) ?Function {
|
||||||
return self.onloadend_cbk;
|
return self.onloadend_cbk;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_onloadstart(self: *XMLHttpRequestEventTarget, handler: Callback, state: *SessionState) !void {
|
pub fn set_onloadstart(self: *XMLHttpRequestEventTarget, handler: Function, state: *SessionState) !void {
|
||||||
if (self.onloadstart_cbk) |cbk| try self.unregister("loadstart", cbk.id);
|
if (self.onloadstart_cbk) |cbk| try self.unregister("loadstart", cbk.id);
|
||||||
try self.register(state.arena, "loadstart", handler);
|
try self.register(state.arena, "loadstart", handler);
|
||||||
self.onloadstart_cbk = handler;
|
self.onloadstart_cbk = handler;
|
||||||
}
|
}
|
||||||
pub fn set_onprogress(self: *XMLHttpRequestEventTarget, handler: Callback, state: *SessionState) !void {
|
pub fn set_onprogress(self: *XMLHttpRequestEventTarget, handler: Function, state: *SessionState) !void {
|
||||||
if (self.onprogress_cbk) |cbk| try self.unregister("progress", cbk.id);
|
if (self.onprogress_cbk) |cbk| try self.unregister("progress", cbk.id);
|
||||||
try self.register(state.arena, "progress", handler);
|
try self.register(state.arena, "progress", handler);
|
||||||
self.onprogress_cbk = handler;
|
self.onprogress_cbk = handler;
|
||||||
}
|
}
|
||||||
pub fn set_onabort(self: *XMLHttpRequestEventTarget, handler: Callback, state: *SessionState) !void {
|
pub fn set_onabort(self: *XMLHttpRequestEventTarget, handler: Function, state: *SessionState) !void {
|
||||||
if (self.onabort_cbk) |cbk| try self.unregister("abort", cbk.id);
|
if (self.onabort_cbk) |cbk| try self.unregister("abort", cbk.id);
|
||||||
try self.register(state.arena, "abort", handler);
|
try self.register(state.arena, "abort", handler);
|
||||||
self.onabort_cbk = handler;
|
self.onabort_cbk = handler;
|
||||||
}
|
}
|
||||||
pub fn set_onload(self: *XMLHttpRequestEventTarget, handler: Callback, state: *SessionState) !void {
|
pub fn set_onload(self: *XMLHttpRequestEventTarget, handler: Function, state: *SessionState) !void {
|
||||||
if (self.onload_cbk) |cbk| try self.unregister("load", cbk.id);
|
if (self.onload_cbk) |cbk| try self.unregister("load", cbk.id);
|
||||||
try self.register(state.arena, "load", handler);
|
try self.register(state.arena, "load", handler);
|
||||||
self.onload_cbk = handler;
|
self.onload_cbk = handler;
|
||||||
}
|
}
|
||||||
pub fn set_ontimeout(self: *XMLHttpRequestEventTarget, handler: Callback, state: *SessionState) !void {
|
pub fn set_ontimeout(self: *XMLHttpRequestEventTarget, handler: Function, state: *SessionState) !void {
|
||||||
if (self.ontimeout_cbk) |cbk| try self.unregister("timeout", cbk.id);
|
if (self.ontimeout_cbk) |cbk| try self.unregister("timeout", cbk.id);
|
||||||
try self.register(state.arena, "timeout", handler);
|
try self.register(state.arena, "timeout", handler);
|
||||||
self.ontimeout_cbk = handler;
|
self.ontimeout_cbk = handler;
|
||||||
}
|
}
|
||||||
pub fn set_onloadend(self: *XMLHttpRequestEventTarget, handler: Callback, state: *SessionState) !void {
|
pub fn set_onloadend(self: *XMLHttpRequestEventTarget, handler: Function, state: *SessionState) !void {
|
||||||
if (self.onloadend_cbk) |cbk| try self.unregister("loadend", cbk.id);
|
if (self.onloadend_cbk) |cbk| try self.unregister("loadend", cbk.id);
|
||||||
try self.register(state.arena, "loadend", handler);
|
try self.register(state.arena, "loadend", handler);
|
||||||
self.onloadend_cbk = handler;
|
self.onloadend_cbk = handler;
|
||||||
|
|||||||
@@ -912,7 +912,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
|||||||
// compatible with. A compatible field has higher precedence
|
// compatible with. A compatible field has higher precedence
|
||||||
// than a coercible, but still isn't a perfect match.
|
// than a coercible, but still isn't a perfect match.
|
||||||
var compatible_index: ?usize = null;
|
var compatible_index: ?usize = null;
|
||||||
|
|
||||||
inline for (u.fields, 0..) |field, i| {
|
inline for (u.fields, 0..) |field, i| {
|
||||||
switch (try self.probeJsValueToZig(named_function, field.type, js_value)) {
|
switch (try self.probeJsValueToZig(named_function, field.type, js_value)) {
|
||||||
.value => |v| return @unionInit(T, field.name, v),
|
.value => |v| return @unionInit(T, field.name, v),
|
||||||
@@ -949,9 +948,9 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
|||||||
// Extracted so that it can be used in both jsValueToZig and in
|
// Extracted so that it can be used in both jsValueToZig and in
|
||||||
// probeJsValueToZig. Avoids having to duplicate this logic when probing.
|
// probeJsValueToZig. Avoids having to duplicate this logic when probing.
|
||||||
fn jsValueToStruct(self: *Scope, comptime named_function: NamedFunction, comptime T: type, js_value: v8.Value) !?T {
|
fn jsValueToStruct(self: *Scope, comptime named_function: NamedFunction, comptime T: type, js_value: v8.Value) !?T {
|
||||||
if (@hasDecl(T, "_CALLBACK_ID_KLUDGE")) {
|
if (@hasDecl(T, "_FUNCTION_ID_KLUDGE")) {
|
||||||
if (!js_value.isFunction()) {
|
if (!js_value.isFunction()) {
|
||||||
return error.InvalidArgument;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const func = v8.Persistent(v8.Function).init(self.isolate, js_value.castTo(v8.Function));
|
const func = v8.Persistent(v8.Function).init(self.isolate, js_value.castTo(v8.Function));
|
||||||
@@ -1220,25 +1219,25 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Callback = struct {
|
pub const Function = struct {
|
||||||
id: usize,
|
id: usize,
|
||||||
scope: *Scope,
|
scope: *Scope,
|
||||||
this: ?v8.Object = null,
|
this: ?v8.Object = null,
|
||||||
func: PersistentFunction,
|
func: PersistentFunction,
|
||||||
|
|
||||||
// We use this when mapping a JS value to a Zig object. We can't
|
// We use this when mapping a JS value to a Zig object. We can't
|
||||||
// Say we have a Zig function that takes a Callback, we can't just
|
// Say we have a Zig function that takes a Function, we can't just
|
||||||
// check param.type == Callback, because Callback is a generic.
|
// check param.type == Function, because Function is a generic.
|
||||||
// So, as a quick hack, we can determine if the Zig type is a
|
// So, as a quick hack, we can determine if the Zig type is a
|
||||||
// callback by checking @hasDecl(T, "_CALLBACK_ID_KLUDGE")
|
// callback by checking @hasDecl(T, "_FUNCTION_ID_KLUDGE")
|
||||||
const _CALLBACK_ID_KLUDGE = true;
|
const _FUNCTION_ID_KLUDGE = true;
|
||||||
|
|
||||||
pub const Result = struct {
|
pub const Result = struct {
|
||||||
stack: ?[]const u8,
|
stack: ?[]const u8,
|
||||||
exception: []const u8,
|
exception: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn withThis(self: *const Callback, value: anytype) !Callback {
|
pub fn withThis(self: *const Function, value: anytype) !Function {
|
||||||
return .{
|
return .{
|
||||||
.id = self.id,
|
.id = self.id,
|
||||||
.func = self.func,
|
.func = self.func,
|
||||||
@@ -1247,20 +1246,20 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(self: *const Callback, args: anytype) !void {
|
pub fn call(self: *const Function, comptime T: type, args: anytype) !T {
|
||||||
return self.callWithThis(self.getThis(), args);
|
return self.callWithThis(T, self.getThis(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tryCall(self: *const Callback, args: anytype, result: *Result) !void {
|
pub fn tryCall(self: *const Function, comptime T: type, args: anytype, result: *Result) !T {
|
||||||
return self.tryCallWithThis(self.getThis(), args, result);
|
return self.tryCallWithThis(T, self.getThis(), args, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tryCallWithThis(self: *const Callback, this: anytype, args: anytype, result: *Result) !void {
|
pub fn tryCallWithThis(self: *const Function, comptime T: type, this: anytype, args: anytype, result: *Result) !void {
|
||||||
var try_catch: TryCatch = undefined;
|
var try_catch: TryCatch = undefined;
|
||||||
try_catch.init(self.scope);
|
try_catch.init(self.scope);
|
||||||
defer try_catch.deinit();
|
defer try_catch.deinit();
|
||||||
|
|
||||||
self.callWithThis(this, args) catch |err| {
|
return self.callWithThis(T, this, args) catch |err| {
|
||||||
if (try_catch.hasCaught()) {
|
if (try_catch.hasCaught()) {
|
||||||
const allocator = self.scope.call_arena;
|
const allocator = self.scope.call_arena;
|
||||||
result.stack = try_catch.stack(allocator) catch null;
|
result.stack = try_catch.stack(allocator) catch null;
|
||||||
@@ -1273,7 +1272,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn callWithThis(self: *const Callback, this: anytype, args: anytype) !void {
|
pub fn callWithThis(self: *const Function, comptime T: type, this: anytype, args: anytype) !T {
|
||||||
const scope = self.scope;
|
const scope = self.scope;
|
||||||
|
|
||||||
const js_this = try scope.valueToExistingObject(this);
|
const js_this = try scope.valueToExistingObject(this);
|
||||||
@@ -1289,14 +1288,18 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
|||||||
if (result == null) {
|
if (result == null) {
|
||||||
return error.JSExecCallback;
|
return error.JSExecCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (@typeInfo(T) == .void) return {};
|
||||||
|
const named_function = comptime NamedFunction.init(T, "callResult");
|
||||||
|
return scope.jsValueToZig(named_function, T, result.?);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getThis(self: *const Callback) v8.Object {
|
fn getThis(self: *const Function) v8.Object {
|
||||||
return self.this orelse self.scope.context.getGlobal();
|
return self.this orelse self.scope.context.getGlobal();
|
||||||
}
|
}
|
||||||
|
|
||||||
// debug/helper to print the source of the JS callback
|
// debug/helper to print the source of the JS callback
|
||||||
pub fn printFunc(self: Callback) !void {
|
pub fn printFunc(self: Function) !void {
|
||||||
const scope = self.scope;
|
const scope = self.scope;
|
||||||
const value = self.func.castToFunction().toValue();
|
const value = self.func.castToFunction().toValue();
|
||||||
const src = try valueToString(scope.call_arena, value, scope.isolate, scope.context);
|
const src = try valueToString(scope.call_arena, value, scope.isolate, scope.context);
|
||||||
@@ -1455,7 +1458,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
|||||||
// Env.JsObject. Want a TypedArray? Env.TypedArray.
|
// Env.JsObject. Want a TypedArray? Env.TypedArray.
|
||||||
pub fn TypedArray(comptime T: type) type {
|
pub fn TypedArray(comptime T: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
// See Callback._CALLBACK_ID_KLUDGE
|
// See Function._FUNCTION_ID_KLUDGE
|
||||||
const _TYPED_ARRAY_ID_KLUDGE = true;
|
const _TYPED_ARRAY_ID_KLUDGE = true;
|
||||||
|
|
||||||
values: []const T,
|
values: []const T,
|
||||||
@@ -1989,7 +1992,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
|||||||
return js_obj.toValue();
|
return js_obj.toValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (T == Callback) {
|
if (T == Function) {
|
||||||
// we're returnig a callback
|
// we're returnig a callback
|
||||||
return value.func.toValue();
|
return value.func.toValue();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user