mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 07:03:29 +00:00
Merge pull request #963 from lightpanda-io/wpt_runner_fix_and_nodeiterator_tweak
Some checks failed
e2e-test / zig build release (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
nightly build / build-linux-x86_64 (push) Has been cancelled
nightly build / build-linux-aarch64 (push) Has been cancelled
nightly build / build-macos-aarch64 (push) Has been cancelled
nightly build / build-macos-x86_64 (push) Has been cancelled
wpt / web platform tests json output (push) Has been cancelled
wpt / perf-fmt (push) Has been cancelled
Some checks failed
e2e-test / zig build release (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
nightly build / build-linux-x86_64 (push) Has been cancelled
nightly build / build-linux-aarch64 (push) Has been cancelled
nightly build / build-macos-aarch64 (push) Has been cancelled
nightly build / build-macos-x86_64 (push) Has been cancelled
wpt / web platform tests json output (push) Has been cancelled
wpt / perf-fmt (push) Has been cancelled
Improves correctness of NodeIterator
This commit is contained in:
@@ -249,12 +249,12 @@ pub const Document = struct {
|
||||
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);
|
||||
pub fn _createTreeWalker(_: *parser.Document, root: *parser.Node, what_to_show: ?TreeWalker.WhatToShow, filter: ?TreeWalker.TreeWalkerOpts) !TreeWalker {
|
||||
return TreeWalker.init(root, what_to_show, filter);
|
||||
}
|
||||
|
||||
pub fn _createNodeIterator(_: *parser.Document, root: *parser.Node, what_to_show: ?u32, filter: ?NodeIterator.NodeIteratorOpts) !NodeIterator {
|
||||
return try NodeIterator.init(root, what_to_show, filter);
|
||||
pub fn _createNodeIterator(_: *parser.Document, root: *parser.Node, what_to_show: ?NodeIterator.WhatToShow, filter: ?NodeIterator.NodeIteratorOpts) !NodeIterator {
|
||||
return NodeIterator.init(root, what_to_show, filter);
|
||||
}
|
||||
|
||||
pub fn getActiveElement(self: *parser.Document, page: *Page) !?*parser.Element {
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
const parser = @import("../netsurf.zig");
|
||||
const Env = @import("../env.zig").Env;
|
||||
const NodeFilter = @import("node_filter.zig");
|
||||
const Node = @import("node.zig").Node;
|
||||
const NodeUnion = @import("node.zig").Union;
|
||||
const DOMException = @import("exceptions.zig").DOMException;
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/NodeIterator
|
||||
// While this is similar to TreeWalker it has its own implementation as there are several subtle differences
|
||||
@@ -29,20 +31,28 @@ const NodeUnion = @import("node.zig").Union;
|
||||
// - nextNode returns the reference node, whereas TreeWalker returns the next node
|
||||
// - Skip and reject are equivalent for NodeIterator, for TreeWalker they are different
|
||||
pub const NodeIterator = struct {
|
||||
pub const Exception = DOMException;
|
||||
|
||||
root: *parser.Node,
|
||||
reference_node: *parser.Node,
|
||||
what_to_show: u32,
|
||||
filter: ?NodeIteratorOpts,
|
||||
filter_func: ?Env.Function,
|
||||
|
||||
pointer_before_current: bool = true,
|
||||
// used to track / block recursive filters
|
||||
is_in_callback: bool = false,
|
||||
|
||||
// One of the few cases where null and undefined resolve to different default.
|
||||
// We need the raw JsObject so that we can probe the tri state:
|
||||
// null, undefined or i32.
|
||||
pub const WhatToShow = Env.JsObject;
|
||||
|
||||
pub const NodeIteratorOpts = union(enum) {
|
||||
function: Env.Function,
|
||||
object: struct { acceptNode: Env.Function },
|
||||
};
|
||||
|
||||
pub fn init(node: *parser.Node, what_to_show: ?u32, filter: ?NodeIteratorOpts) !NodeIterator {
|
||||
pub fn init(node: *parser.Node, what_to_show_: ?WhatToShow, filter: ?NodeIteratorOpts) !NodeIterator {
|
||||
var filter_func: ?Env.Function = null;
|
||||
if (filter) |f| {
|
||||
filter_func = switch (f) {
|
||||
@@ -51,10 +61,21 @@ pub const NodeIterator = struct {
|
||||
};
|
||||
}
|
||||
|
||||
var what_to_show: u32 = undefined;
|
||||
if (what_to_show_) |wts| {
|
||||
switch (try wts.triState(NodeIterator, "what_to_show", u32)) {
|
||||
.null => what_to_show = 0,
|
||||
.undefined => what_to_show = NodeFilter.NodeFilter._SHOW_ALL,
|
||||
.value => |v| what_to_show = v,
|
||||
}
|
||||
} else {
|
||||
what_to_show = NodeFilter.NodeFilter._SHOW_ALL;
|
||||
}
|
||||
|
||||
return .{
|
||||
.root = node,
|
||||
.reference_node = node,
|
||||
.what_to_show = what_to_show orelse NodeFilter.NodeFilter._SHOW_ALL,
|
||||
.what_to_show = what_to_show,
|
||||
.filter = filter,
|
||||
.filter_func = filter_func,
|
||||
};
|
||||
@@ -81,9 +102,13 @@ pub const NodeIterator = struct {
|
||||
}
|
||||
|
||||
pub fn _nextNode(self: *NodeIterator) !?NodeUnion {
|
||||
if (self.pointer_before_current) { // Unlike TreeWalker, NodeIterator starts at the first node
|
||||
self.pointer_before_current = false;
|
||||
try self.callbackStart();
|
||||
defer self.callbackEnd();
|
||||
|
||||
if (self.pointer_before_current) {
|
||||
// Unlike TreeWalker, NodeIterator starts at the first node
|
||||
if (.accept == try NodeFilter.verify(self.what_to_show, self.filter_func, self.reference_node)) {
|
||||
self.pointer_before_current = false;
|
||||
return try Node.toInterface(self.reference_node);
|
||||
}
|
||||
}
|
||||
@@ -107,13 +132,19 @@ pub const NodeIterator = struct {
|
||||
}
|
||||
|
||||
pub fn _previousNode(self: *NodeIterator) !?NodeUnion {
|
||||
try self.callbackStart();
|
||||
defer self.callbackEnd();
|
||||
|
||||
if (!self.pointer_before_current) {
|
||||
self.pointer_before_current = true;
|
||||
if (.accept == try NodeFilter.verify(self.what_to_show, self.filter_func, self.reference_node)) {
|
||||
return try Node.toInterface(self.reference_node); // Still need to verify as last may be first as well
|
||||
self.pointer_before_current = true;
|
||||
// Still need to verify as last may be first as well
|
||||
return try Node.toInterface(self.reference_node);
|
||||
}
|
||||
}
|
||||
if (self.reference_node == self.root) return null;
|
||||
if (self.reference_node == self.root) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var current = self.reference_node;
|
||||
while (try parser.nodePreviousSibling(current)) |previous| {
|
||||
@@ -151,6 +182,11 @@ pub const NodeIterator = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn _detach(self: *const NodeIterator) void {
|
||||
// no-op as per spec
|
||||
_ = self;
|
||||
}
|
||||
|
||||
fn firstChild(self: *const NodeIterator, node: *parser.Node) !?*parser.Node {
|
||||
const children = try parser.nodeGetChildNodes(node);
|
||||
const child_count = try parser.nodeListLength(children);
|
||||
@@ -217,6 +253,18 @@ pub const NodeIterator = struct {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn callbackStart(self: *NodeIterator) !void {
|
||||
if (self.is_in_callback) {
|
||||
// this is the correct DOMExeption
|
||||
return error.InvalidState;
|
||||
}
|
||||
self.is_in_callback = true;
|
||||
}
|
||||
|
||||
fn callbackEnd(self: *NodeIterator) void {
|
||||
self.is_in_callback = false;
|
||||
}
|
||||
};
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
|
||||
@@ -33,12 +33,17 @@ pub const TreeWalker = struct {
|
||||
filter: ?TreeWalkerOpts,
|
||||
filter_func: ?Env.Function,
|
||||
|
||||
// One of the few cases where null and undefined resolve to different default.
|
||||
// We need the raw JsObject so that we can probe the tri state:
|
||||
// null, undefined or i32.
|
||||
pub const WhatToShow = Env.JsObject;
|
||||
|
||||
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 {
|
||||
pub fn init(node: *parser.Node, what_to_show_: ?WhatToShow, filter: ?TreeWalkerOpts) !TreeWalker {
|
||||
var filter_func: ?Env.Function = null;
|
||||
|
||||
if (filter) |f| {
|
||||
@@ -48,10 +53,21 @@ pub const TreeWalker = struct {
|
||||
};
|
||||
}
|
||||
|
||||
var what_to_show: u32 = undefined;
|
||||
if (what_to_show_) |wts| {
|
||||
switch (try wts.triState(TreeWalker, "what_to_show", u32)) {
|
||||
.null => what_to_show = 0,
|
||||
.undefined => what_to_show = NodeFilter.NodeFilter._SHOW_ALL,
|
||||
.value => |v| what_to_show = v,
|
||||
}
|
||||
} else {
|
||||
what_to_show = NodeFilter.NodeFilter._SHOW_ALL;
|
||||
}
|
||||
|
||||
return .{
|
||||
.root = node,
|
||||
.current_node = node,
|
||||
.what_to_show = what_to_show orelse NodeFilter.NodeFilter._SHOW_ALL,
|
||||
.what_to_show = what_to_show,
|
||||
.filter = filter,
|
||||
.filter_func = filter_func,
|
||||
};
|
||||
|
||||
@@ -263,22 +263,25 @@ const Writer = struct {
|
||||
if (line.len == 0) {
|
||||
break;
|
||||
}
|
||||
var fields = std.mem.splitScalar(u8, line, '|');
|
||||
const case_name = fields.next() orelse {
|
||||
std.debug.print("invalid result line: {s}\n", .{line});
|
||||
return error.InvalidResult;
|
||||
};
|
||||
// case names can have | in them, so we can't simply split on |
|
||||
var case_name = line;
|
||||
var case_pass = false; // so pessimistic!
|
||||
var case_message: []const u8 = "";
|
||||
|
||||
const text_status = fields.next() orelse {
|
||||
std.debug.print("invalid result line: {s}\n", .{line});
|
||||
return error.InvalidResult;
|
||||
};
|
||||
|
||||
const case_pass = std.mem.eql(u8, text_status, "Pass");
|
||||
if (case_pass) {
|
||||
if (std.mem.endsWith(u8, line, "|Pass")) {
|
||||
case_name = line[0 .. line.len - 5];
|
||||
case_pass = true;
|
||||
case_pass_count += 1;
|
||||
} else {
|
||||
// If 1 case fails, we treat the entire file as a fail.
|
||||
// both cases names and messages can have | in them. Our only
|
||||
// chance to "parse" this is to anchor off the |Fail.
|
||||
const pos = std.mem.indexOf(u8, line, "|Fail") orelse {
|
||||
std.debug.print("invalid result line: {s}\n", .{line});
|
||||
return error.InvalidResult;
|
||||
};
|
||||
|
||||
case_name = line[0..pos];
|
||||
case_message = line[pos + 1 ..];
|
||||
pass = false;
|
||||
case_fail_count += 1;
|
||||
}
|
||||
@@ -286,7 +289,7 @@ const Writer = struct {
|
||||
try cases.append(self.arena, .{
|
||||
.name = case_name,
|
||||
.pass = case_pass,
|
||||
.message = fields.next(),
|
||||
.message = case_message,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -941,6 +941,27 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
fn jsValueToZig(self: *JsContext, comptime named_function: NamedFunction, comptime T: type, js_value: v8.Value) !T {
|
||||
switch (@typeInfo(T)) {
|
||||
.optional => |o| {
|
||||
if (comptime isJsObject(o.child)) {
|
||||
// If type type is a ?JsObject, then we want to pass
|
||||
// a JsObject, not null. Consider a function,
|
||||
// _doSomething(arg: ?Env.JsObjet) void { ... }
|
||||
//
|
||||
// And then these two calls:
|
||||
// doSomething();
|
||||
// doSomething(null);
|
||||
//
|
||||
// In the first case, we'll pass `null`. But in the
|
||||
// second, we'll pass a JsObject which represents
|
||||
// null.
|
||||
// If we don't have this code, both cases will
|
||||
// pass in `null` and the the doSomething won't
|
||||
// be able to tell if `null` was explicitly passed
|
||||
// or whether no parameter was passed.
|
||||
return JsObject{
|
||||
.js_obj = js_value.castTo(v8.Object),
|
||||
.js_context = self,
|
||||
};
|
||||
}
|
||||
if (js_value.isNullOrUndefined()) {
|
||||
return null;
|
||||
}
|
||||
@@ -1938,6 +1959,24 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
return try js_context.createFunction(js_value);
|
||||
}
|
||||
|
||||
pub fn isNull(self: JsObject) bool {
|
||||
return self.js_obj.toValue().isNull();
|
||||
}
|
||||
|
||||
pub fn isUndefined(self: JsObject) bool {
|
||||
return self.js_obj.toValue().isUndefined();
|
||||
}
|
||||
|
||||
pub fn triState(self: JsObject, comptime Struct: type, comptime name: []const u8, comptime T: type) !TriState(T) {
|
||||
if (self.isNull()) {
|
||||
return .{ .null = {} };
|
||||
}
|
||||
if (self.isUndefined()) {
|
||||
return .{ .undefined = {} };
|
||||
}
|
||||
return .{ .value = try self.toZig(Struct, name, T) };
|
||||
}
|
||||
|
||||
pub fn isNullOrUndefined(self: JsObject) bool {
|
||||
return self.js_obj.toValue().isNullOrUndefined();
|
||||
}
|
||||
@@ -1965,6 +2004,14 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
const named_function = comptime NamedFunction.init(Struct, name);
|
||||
return self.js_context.jsValueToZig(named_function, T, self.js_obj.toValue());
|
||||
}
|
||||
|
||||
pub fn TriState(comptime T: type) type {
|
||||
return union(enum) {
|
||||
null: void,
|
||||
undefined: void,
|
||||
value: T,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// This only exists so that we know whether a function wants the opaque
|
||||
|
||||
Reference in New Issue
Block a user