mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 14:33:47 +00:00
various small api fixes/tweaks
This commit is contained in:
@@ -63,7 +63,7 @@ _size_144_8: MemoryPoolAligned([144]u8, .@"8"),
|
|||||||
_size_152_8: MemoryPoolAligned([152]u8, .@"8"),
|
_size_152_8: MemoryPoolAligned([152]u8, .@"8"),
|
||||||
_size_160_8: MemoryPoolAligned([160]u8, .@"8"),
|
_size_160_8: MemoryPoolAligned([160]u8, .@"8"),
|
||||||
_size_184_8: MemoryPoolAligned([184]u8, .@"8"),
|
_size_184_8: MemoryPoolAligned([184]u8, .@"8"),
|
||||||
_size_192_8: MemoryPoolAligned([192]u8, .@"8"),
|
_size_232_8: MemoryPoolAligned([232]u8, .@"8"),
|
||||||
_size_648_8: MemoryPoolAligned([648]u8, .@"8"),
|
_size_648_8: MemoryPoolAligned([648]u8, .@"8"),
|
||||||
|
|
||||||
pub fn init(page: *Page) Factory {
|
pub fn init(page: *Page) Factory {
|
||||||
@@ -86,7 +86,7 @@ pub fn init(page: *Page) Factory {
|
|||||||
._size_152_8 = MemoryPoolAligned([152]u8, .@"8").init(page.arena),
|
._size_152_8 = MemoryPoolAligned([152]u8, .@"8").init(page.arena),
|
||||||
._size_160_8 = MemoryPoolAligned([160]u8, .@"8").init(page.arena),
|
._size_160_8 = MemoryPoolAligned([160]u8, .@"8").init(page.arena),
|
||||||
._size_184_8 = MemoryPoolAligned([184]u8, .@"8").init(page.arena),
|
._size_184_8 = MemoryPoolAligned([184]u8, .@"8").init(page.arena),
|
||||||
._size_192_8 = MemoryPoolAligned([192]u8, .@"8").init(page.arena),
|
._size_232_8 = MemoryPoolAligned([232]u8, .@"8").init(page.arena),
|
||||||
._size_648_8 = MemoryPoolAligned([648]u8, .@"8").init(page.arena),
|
._size_648_8 = MemoryPoolAligned([648]u8, .@"8").init(page.arena),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -265,7 +265,7 @@ pub fn createT(self: *Factory, comptime T: type) !*T {
|
|||||||
if (comptime SO == 152) return @ptrCast(try self._size_152_8.create());
|
if (comptime SO == 152) return @ptrCast(try self._size_152_8.create());
|
||||||
if (comptime SO == 160) return @ptrCast(try self._size_160_8.create());
|
if (comptime SO == 160) return @ptrCast(try self._size_160_8.create());
|
||||||
if (comptime SO == 184) return @ptrCast(try self._size_184_8.create());
|
if (comptime SO == 184) return @ptrCast(try self._size_184_8.create());
|
||||||
if (comptime SO == 192) return @ptrCast(try self._size_192_8.create());
|
if (comptime SO == 232) return @ptrCast(try self._size_232_8.create());
|
||||||
if (comptime SO == 648) return @ptrCast(try self._size_648_8.create());
|
if (comptime SO == 648) return @ptrCast(try self._size_648_8.create());
|
||||||
@compileError(std.fmt.comptimePrint("No pool configured for @sizeOf({d}), @alignOf({d}): ({s})", .{ SO, @alignOf(T), @typeName(T) }));
|
@compileError(std.fmt.comptimePrint("No pool configured for @sizeOf({d}), @alignOf({d}): ({s})", .{ SO, @alignOf(T), @typeName(T) }));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ pub fn navigate(self: *Page, request_url: [:0]const u8, opts: NavigateOpts) !voi
|
|||||||
try self.reset(false);
|
try self.reset(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(.http, "navigate", .{
|
log.info(.page, "navigate", .{
|
||||||
.url = request_url,
|
.url = request_url,
|
||||||
.method = opts.method,
|
.method = opts.method,
|
||||||
.reason = opts.reason,
|
.reason = opts.reason,
|
||||||
@@ -329,7 +329,7 @@ pub fn navigate(self: *Page, request_url: [:0]const u8, opts: NavigateOpts) !voi
|
|||||||
.done_callback = pageDoneCallback,
|
.done_callback = pageDoneCallback,
|
||||||
.error_callback = pageErrorCallback,
|
.error_callback = pageErrorCallback,
|
||||||
}) catch |err| {
|
}) catch |err| {
|
||||||
log.err(.http, "navigate request", .{ .url = self.url, .err = err });
|
log.err(.page, "navigate request", .{ .url = self.url, .err = err });
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -412,7 +412,7 @@ fn pageHeaderDoneCallback(transfer: *Http.Transfer) !void {
|
|||||||
self.window._location = try Location.init(self.url, self);
|
self.window._location = try Location.init(self.url, self);
|
||||||
self.document._location = self.window._location;
|
self.document._location = self.window._location;
|
||||||
|
|
||||||
log.debug(.http, "navigate header", .{
|
log.debug(.page, "navigate header", .{
|
||||||
.url = self.url,
|
.url = self.url,
|
||||||
.status = header.status,
|
.status = header.status,
|
||||||
.content_type = header.contentType(),
|
.content_type = header.contentType(),
|
||||||
@@ -433,7 +433,7 @@ fn pageDataCallback(transfer: *Http.Transfer, data: []const u8) !void {
|
|||||||
} orelse .unknown;
|
} orelse .unknown;
|
||||||
|
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
log.debug(.http, "navigate first chunk", .{ .content_type = mime.content_type, .len = data.len });
|
log.debug(.page, "navigate first chunk", .{ .content_type = mime.content_type, .len = data.len });
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (mime.content_type) {
|
switch (mime.content_type) {
|
||||||
@@ -475,7 +475,7 @@ fn pageDataCallback(transfer: *Http.Transfer, data: []const u8) !void {
|
|||||||
|
|
||||||
fn pageDoneCallback(ctx: *anyopaque) !void {
|
fn pageDoneCallback(ctx: *anyopaque) !void {
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
log.debug(.http, "navigate done", .{});
|
log.debug(.page, "navigate done", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
var self: *Page = @ptrCast(@alignCast(ctx));
|
var self: *Page = @ptrCast(@alignCast(ctx));
|
||||||
@@ -522,7 +522,7 @@ fn pageDoneCallback(ctx: *anyopaque) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn pageErrorCallback(ctx: *anyopaque, err: anyerror) void {
|
fn pageErrorCallback(ctx: *anyopaque, err: anyerror) void {
|
||||||
log.err(.http, "navigate failed", .{ .err = err });
|
log.err(.page, "navigate failed", .{ .err = err });
|
||||||
|
|
||||||
var self: *Page = @ptrCast(@alignCast(ctx));
|
var self: *Page = @ptrCast(@alignCast(ctx));
|
||||||
self.clearTransferArena();
|
self.clearTransferArena();
|
||||||
@@ -624,7 +624,7 @@ fn _wait(self: *Page, wait_ms: u32) !Session.WaitResult {
|
|||||||
|
|
||||||
if (try_catch.hasCaught()) {
|
if (try_catch.hasCaught()) {
|
||||||
const msg = (try try_catch.err(self.arena)) orelse "unknown";
|
const msg = (try try_catch.err(self.arena)) orelse "unknown";
|
||||||
log.warn(.user_script, "page wait", .{ .err = msg, .src = "scheduler" });
|
log.warn(.js, "page wait", .{ .err = msg, .src = "scheduler" });
|
||||||
return error.JsError;
|
return error.JsError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -757,7 +757,7 @@ const Script = struct {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
const msg = try_catch.err(page.arena) catch |err| @errorName(err) orelse "unknown";
|
const msg = try_catch.err(page.arena) catch |err| @errorName(err) orelse "unknown";
|
||||||
log.warn(.user_script, "eval script", .{
|
log.warn(.js, "eval script", .{
|
||||||
.url = url,
|
.url = url,
|
||||||
.err = msg,
|
.err = msg,
|
||||||
.cacheable = cacheable,
|
.cacheable = cacheable,
|
||||||
|
|||||||
@@ -17,24 +17,45 @@
|
|||||||
// 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 std = @import("std");
|
const std = @import("std");
|
||||||
|
const Page = @import("Page.zig");
|
||||||
const Node = @import("webapi/Node.zig");
|
const Node = @import("webapi/Node.zig");
|
||||||
|
|
||||||
pub const Opts = struct {
|
pub const RootOpts = struct {
|
||||||
// @ZIGDOM (none of these do anything)
|
|
||||||
with_base: bool = false,
|
with_base: bool = false,
|
||||||
strip_mode: StripMode = .{},
|
strip: Opts.Strip = .{},
|
||||||
|
};
|
||||||
|
|
||||||
pub const StripMode = struct {
|
pub const Opts = struct {
|
||||||
|
strip: Strip = .{},
|
||||||
|
pub const Strip = struct {
|
||||||
js: bool = false,
|
js: bool = false,
|
||||||
ui: bool = false,
|
ui: bool = false,
|
||||||
css: bool = false,
|
css: bool = false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn root(opts: RootOpts, writer: *std.Io.Writer, page: *Page) !void {
|
||||||
|
const doc = page.document;
|
||||||
|
if (opts.with_base) {
|
||||||
|
if (doc.is(Node.Document.HTMLDocument)) |html_doc| {
|
||||||
|
const parent = if (html_doc.getHead()) |head| head.asNode() else doc.asNode();
|
||||||
|
const base = try doc.createElement("base", null, page);
|
||||||
|
try base.setAttributeSafe("base", page.url, page);
|
||||||
|
_ = try parent.insertBefore(base.asNode(), parent.firstChild(), page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return deep(doc.asNode(), .{.strip = opts.strip}, writer);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn deep(node: *Node, opts: Opts, writer: *std.Io.Writer) error{WriteFailed}!void {
|
pub fn deep(node: *Node, opts: Opts, writer: *std.Io.Writer) error{WriteFailed}!void {
|
||||||
switch (node._type) {
|
switch (node._type) {
|
||||||
.cdata => |cd| try writer.writeAll(cd.getData()),
|
.cdata => |cd| try writer.writeAll(cd.getData()),
|
||||||
.element => |el| {
|
.element => |el| {
|
||||||
|
if (shouldStripElement(el, opts)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try el.format(writer);
|
try el.format(writer);
|
||||||
try children(node, opts, writer);
|
try children(node, opts, writer);
|
||||||
if (!isVoidElement(el)) {
|
if (!isVoidElement(el)) {
|
||||||
@@ -106,3 +127,47 @@ fn isVoidElement(el: *const Node.Element) bool {
|
|||||||
.svg => false,
|
.svg => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn shouldStripElement(el: *const Node.Element, opts: Opts) bool {
|
||||||
|
const tag_name = el.getTagNameDump();
|
||||||
|
|
||||||
|
if (opts.strip.js) {
|
||||||
|
if (std.mem.eql(u8, tag_name, "script")) return true;
|
||||||
|
if (std.mem.eql(u8, tag_name, "noscript")) return true;
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, tag_name, "link")) {
|
||||||
|
if (el.getAttributeSafe("as")) |as| {
|
||||||
|
if (std.mem.eql(u8, as, "script")) return true;
|
||||||
|
}
|
||||||
|
if (el.getAttributeSafe("rel")) |rel| {
|
||||||
|
if (std.mem.eql(u8, rel, "modulepreload") or std.mem.eql(u8, rel, "preload")) {
|
||||||
|
if (el.getAttributeSafe("as")) |as| {
|
||||||
|
if (std.mem.eql(u8, as, "script")) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.strip.css or opts.strip.ui) {
|
||||||
|
if (std.mem.eql(u8, tag_name, "style")) return true;
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, tag_name, "link")) {
|
||||||
|
if (el.getAttributeSafe("rel")) |rel| {
|
||||||
|
if (std.mem.eql(u8, rel, "stylesheet")) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.strip.ui) {
|
||||||
|
if (std.mem.eql(u8, tag_name, "img")) return true;
|
||||||
|
if (std.mem.eql(u8, tag_name, "picture")) return true;
|
||||||
|
if (std.mem.eql(u8, tag_name, "video")) return true;
|
||||||
|
if (std.mem.eql(u8, tag_name, "audio")) return true;
|
||||||
|
if (std.mem.eql(u8, tag_name, "svg")) return true;
|
||||||
|
if (std.mem.eql(u8, tag_name, "canvas")) return true;
|
||||||
|
if (std.mem.eql(u8, tag_name, "iframe")) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ pub fn callWithThis(self: *const Function, comptime T: type, this: anytype, args
|
|||||||
|
|
||||||
const result = self.func.castToFunction().call(context.v8_context, js_this, js_args);
|
const result = self.func.castToFunction().call(context.v8_context, js_this, js_args);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
|
std.debug.print("CB ERR: {s}\n", .{self.src() catch "???"});
|
||||||
return error.JSExecCallback;
|
return error.JSExecCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,3 +80,11 @@
|
|||||||
testing.expectTrue(whereResult.length >= 3);
|
testing.expectTrue(whereResult.length >= 3);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div id=escaped class=":popover-open"></div>
|
||||||
|
<script id="escaped">
|
||||||
|
{
|
||||||
|
const escaped = document.querySelector('.\\:popover-open');
|
||||||
|
testing.expectEqual('escaped', escaped.id);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ pub fn pushState(self: *History, state: js.Object, _title: []const u8, url: ?[]c
|
|||||||
_ = url; // For minimal implementation, we don't actually navigate
|
_ = url; // For minimal implementation, we don't actually navigate
|
||||||
_ = page;
|
_ = page;
|
||||||
|
|
||||||
self._state = state;
|
self._state = try state.persist();
|
||||||
self._length += 1;
|
self._length += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ pub fn replaceState(self: *History, state: js.Object, _title: []const u8, url: ?
|
|||||||
_ = _title;
|
_ = _title;
|
||||||
_ = url;
|
_ = url;
|
||||||
_ = page;
|
_ = page;
|
||||||
self._state = state;
|
self._state = try state.persist();
|
||||||
// Note: replaceState doesn't change length
|
// Note: replaceState doesn't change length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ _performance: Performance,
|
|||||||
_history: History,
|
_history: History,
|
||||||
_storage_bucket: *storage.Bucket,
|
_storage_bucket: *storage.Bucket,
|
||||||
_on_load: ?js.Function = null,
|
_on_load: ?js.Function = null,
|
||||||
|
_on_error: ?js.Function = null, // TODO: invoke on error?
|
||||||
_location: *Location,
|
_location: *Location,
|
||||||
_timer_id: u30 = 0,
|
_timer_id: u30 = 0,
|
||||||
_timers: std.AutoHashMapUnmanaged(u32, *ScheduleCallback) = .{},
|
_timers: std.AutoHashMapUnmanaged(u32, *ScheduleCallback) = .{},
|
||||||
@@ -71,7 +72,6 @@ pub fn getDocument(self: *Window) *Document {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn getConsole(self: *Window) *Console {
|
pub fn getConsole(self: *Window) *Console {
|
||||||
std.debug.print("getConsole\n", .{});
|
|
||||||
return &self._console;
|
return &self._console;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +119,18 @@ pub fn setOnLoad(self: *Window, cb_: ?js.Function) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getOnError(self: *const Window) ?js.Function {
|
||||||
|
return self._on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setOnError(self: *Window, cb_: ?js.Function) !void {
|
||||||
|
if (cb_) |cb| {
|
||||||
|
self._on_error = cb;
|
||||||
|
} else {
|
||||||
|
self._on_error = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fetch(_: *const Window, input: Fetch.Input, page: *Page) !js.Promise {
|
pub fn fetch(_: *const Window, input: Fetch.Input, page: *Page) !js.Promise {
|
||||||
return Fetch.init(input, page);
|
return Fetch.init(input, page);
|
||||||
}
|
}
|
||||||
@@ -214,7 +226,7 @@ pub fn matchMedia(_: *const Window, query: []const u8, page: *Page) !*MediaQuery
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getComputedStyle(_: *const Window, _: *Element, page: *Page) !@import("css/CSSStyleDeclaration.zig") {
|
pub fn getComputedStyle(_: *const Window, _: *Element, page: *Page) !*CSSStyleDeclaration {
|
||||||
return CSSStyleDeclaration.init(null, page);
|
return CSSStyleDeclaration.init(null, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,6 +374,7 @@ pub const JsApi = struct {
|
|||||||
pub const crypto = bridge.accessor(Window.getCrypto, null, .{ .cache = "crypto" });
|
pub const crypto = bridge.accessor(Window.getCrypto, null, .{ .cache = "crypto" });
|
||||||
pub const customElements = bridge.accessor(Window.getCustomElements, null, .{ .cache = "customElements" });
|
pub const customElements = bridge.accessor(Window.getCustomElements, null, .{ .cache = "customElements" });
|
||||||
pub const onload = bridge.accessor(Window.getOnLoad, Window.setOnLoad, .{});
|
pub const onload = bridge.accessor(Window.getOnLoad, Window.setOnLoad, .{});
|
||||||
|
pub const onerror = bridge.accessor(Window.getOnError, Window.getOnError, .{});
|
||||||
pub const fetch = bridge.function(Window.fetch, .{});
|
pub const fetch = bridge.function(Window.fetch, .{});
|
||||||
pub const queueMicrotask = bridge.function(Window.queueMicrotask, .{});
|
pub const queueMicrotask = bridge.function(Window.queueMicrotask, .{});
|
||||||
pub const setTimeout = bridge.function(Window.setTimeout, .{});
|
pub const setTimeout = bridge.function(Window.setTimeout, .{});
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ pub fn getMatches(_: *const MediaQueryList) bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn addListener(_: *const MediaQueryList, _: js.Function) void {}
|
||||||
|
pub fn removeListener(_: *const MediaQueryList, _: js.Function) void {}
|
||||||
|
|
||||||
pub const JsApi = struct {
|
pub const JsApi = struct {
|
||||||
pub const bridge = js.Bridge(MediaQueryList);
|
pub const bridge = js.Bridge(MediaQueryList);
|
||||||
|
|
||||||
@@ -54,6 +57,8 @@ pub const JsApi = struct {
|
|||||||
|
|
||||||
pub const media = bridge.accessor(MediaQueryList.getMedia, null, .{});
|
pub const media = bridge.accessor(MediaQueryList.getMedia, null, .{});
|
||||||
pub const matches = bridge.accessor(MediaQueryList.getMatches, null, .{});
|
pub const matches = bridge.accessor(MediaQueryList.getMatches, null, .{});
|
||||||
|
pub const addListener = bridge.function(MediaQueryList.addListener, .{});
|
||||||
|
pub const removeListener = bridge.function(MediaQueryList.removeListener, .{});
|
||||||
};
|
};
|
||||||
|
|
||||||
const testing = @import("../../../testing.zig");
|
const testing = @import("../../../testing.zig");
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ _executed: bool = false,
|
|||||||
pub fn asElement(self: *Script) *Element {
|
pub fn asElement(self: *Script) *Element {
|
||||||
return self._proto._proto;
|
return self._proto._proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn asConstElement(self: *const Script) *const Element {
|
||||||
|
return self._proto._proto;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asNode(self: *Script) *Node {
|
pub fn asNode(self: *Script) *Node {
|
||||||
return self.asElement().asNode();
|
return self.asElement().asNode();
|
||||||
}
|
}
|
||||||
@@ -76,6 +81,10 @@ pub fn setOnError(self: *Script, cb_: ?js.Function) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getNoModule(self: *const Script) bool {
|
||||||
|
return self.asConstElement().getAttributeSafe("nomodule") != null;
|
||||||
|
}
|
||||||
|
|
||||||
pub const JsApi = struct {
|
pub const JsApi = struct {
|
||||||
pub const bridge = js.Bridge(Script);
|
pub const bridge = js.Bridge(Script);
|
||||||
|
|
||||||
@@ -88,6 +97,7 @@ pub const JsApi = struct {
|
|||||||
pub const src = bridge.accessor(Script.getSrc, Script.setSrc, .{});
|
pub const src = bridge.accessor(Script.getSrc, Script.setSrc, .{});
|
||||||
pub const onload = bridge.accessor(Script.getOnLoad, Script.setOnLoad, .{});
|
pub const onload = bridge.accessor(Script.getOnLoad, Script.setOnLoad, .{});
|
||||||
pub const onerorr = bridge.accessor(Script.getOnError, Script.setOnError, .{});
|
pub const onerorr = bridge.accessor(Script.getOnError, Script.setOnError, .{});
|
||||||
|
pub const noModule = bridge.accessor(Script.getNoModule, null, .{});
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Build = struct {
|
pub const Build = struct {
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ pub fn open(self: *XMLHttpRequest, method_: []const u8, url: [:0]const u8) !void
|
|||||||
|
|
||||||
pub fn send(self: *XMLHttpRequest, body_: ?[]const u8) !void {
|
pub fn send(self: *XMLHttpRequest, body_: ?[]const u8) !void {
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
log.debug(.xhr, "XMLHttpRequest.send", .{ .url = self._url });
|
log.debug(.http, "XMLHttpRequest.send", .{ .url = self._url });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body_) |b| {
|
if (body_) |b| {
|
||||||
|
|||||||
@@ -226,8 +226,8 @@ pub fn parse(arena: Allocator, input: []const u8, page: *Page) ParseError!Select
|
|||||||
|
|
||||||
fn parsePart(self: *Parser, arena: Allocator, page: *Page) !Part {
|
fn parsePart(self: *Parser, arena: Allocator, page: *Page) !Part {
|
||||||
return switch (self.peek()) {
|
return switch (self.peek()) {
|
||||||
'#' => .{ .id = try self.id() },
|
'#' => .{ .id = try self.id(arena) },
|
||||||
'.' => .{ .class = try self.class() },
|
'.' => .{ .class = try self.class(arena) },
|
||||||
'*' => blk: {
|
'*' => blk: {
|
||||||
self.input = self.input[1..];
|
self.input = self.input[1..];
|
||||||
break :blk .universal;
|
break :blk .universal;
|
||||||
@@ -655,7 +655,7 @@ fn parseNthPattern(self: *Parser) !Selector.NthPattern {
|
|||||||
return .{ .a = a, .b = b };
|
return .{ .a = a, .b = b };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(self: *Parser) ![]const u8 {
|
pub fn id(self: *Parser, arena: Allocator) ![]const u8 {
|
||||||
// Must be called when we're at a '#'
|
// Must be called when we're at a '#'
|
||||||
std.debug.assert(self.peek() == '#');
|
std.debug.assert(self.peek() == '#');
|
||||||
|
|
||||||
@@ -667,26 +667,46 @@ pub fn id(self: *Parser) ![]const u8 {
|
|||||||
return error.InvalidIDSelector;
|
return error.InvalidIDSelector;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First character: must be letter, underscore, or non-ASCII (>= 0x80)
|
// First pass: find the end of the id and check if there are escape sequences
|
||||||
// Can also be hyphen if not followed by digit or another hyphen
|
var i: usize = 0;
|
||||||
const first = input[0];
|
var has_escape = false;
|
||||||
if (first == '-') {
|
var first_char_validated = false;
|
||||||
if (input.len < 2) {
|
|
||||||
|
while (i < input.len) {
|
||||||
|
const b = input[i];
|
||||||
|
|
||||||
|
if (b == '\\') {
|
||||||
|
// Escape sequence
|
||||||
|
if (i + 1 >= input.len) {
|
||||||
@branchHint(.cold);
|
@branchHint(.cold);
|
||||||
return error.InvalidIDSelector;
|
return error.InvalidIDSelector;
|
||||||
}
|
}
|
||||||
const second = input[1];
|
has_escape = true;
|
||||||
|
i += 2; // Skip backslash and escaped char
|
||||||
|
first_char_validated = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate first character if not yet validated
|
||||||
|
if (!first_char_validated) {
|
||||||
|
if (b == '-') {
|
||||||
|
if (i + 1 >= input.len) {
|
||||||
|
@branchHint(.cold);
|
||||||
|
return error.InvalidIDSelector;
|
||||||
|
}
|
||||||
|
const second = input[i + 1];
|
||||||
if (second == '-' or std.ascii.isDigit(second)) {
|
if (second == '-' or std.ascii.isDigit(second)) {
|
||||||
@branchHint(.cold);
|
@branchHint(.cold);
|
||||||
return error.InvalidIDSelector;
|
return error.InvalidIDSelector;
|
||||||
}
|
}
|
||||||
} else if (!std.ascii.isAlphabetic(first) and first != '_' and first < 0x80) {
|
} else if (!std.ascii.isAlphabetic(b) and b != '_' and b < 0x80) {
|
||||||
@branchHint(.cold);
|
@branchHint(.cold);
|
||||||
return error.InvalidIDSelector;
|
return error.InvalidIDSelector;
|
||||||
}
|
}
|
||||||
|
first_char_validated = true;
|
||||||
|
}
|
||||||
|
|
||||||
var i: usize = 1;
|
// Check if this is a valid id character
|
||||||
for (input[1..]) |b| {
|
|
||||||
switch (b) {
|
switch (b) {
|
||||||
'a'...'z', 'A'...'Z', '0'...'9', '-', '_' => {},
|
'a'...'z', 'A'...'Z', '0'...'9', '-', '_' => {},
|
||||||
0x80...0xFF => {}, // non-ASCII characters
|
0x80...0xFF => {}, // non-ASCII characters
|
||||||
@@ -701,11 +721,39 @@ pub fn id(self: *Parser) ![]const u8 {
|
|||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.input = input[i..];
|
if (i == 0) {
|
||||||
return input[0..i];
|
@branchHint(.cold);
|
||||||
|
return error.InvalidIDSelector;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class(self: *Parser) ![]const u8 {
|
const raw = input[0..i];
|
||||||
|
self.input = input[i..];
|
||||||
|
|
||||||
|
// If no escape sequences, return the slice as-is
|
||||||
|
if (!has_escape) {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build unescaped string
|
||||||
|
var result = try std.ArrayList(u8).initCapacity(arena, raw.len);
|
||||||
|
var j: usize = 0;
|
||||||
|
while (j < raw.len) {
|
||||||
|
if (raw[j] == '\\') {
|
||||||
|
j += 1; // Skip backslash
|
||||||
|
if (j < raw.len) {
|
||||||
|
try result.append(arena, raw[j]); // Add escaped char
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try result.append(arena, raw[j]);
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.items;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn class(self: *Parser, arena: Allocator) ![]const u8 {
|
||||||
// Must be called when we're at a '.'
|
// Must be called when we're at a '.'
|
||||||
std.debug.assert(self.peek() == '.');
|
std.debug.assert(self.peek() == '.');
|
||||||
|
|
||||||
@@ -717,26 +765,46 @@ fn class(self: *Parser) ![]const u8 {
|
|||||||
return error.InvalidClassSelector;
|
return error.InvalidClassSelector;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First character: must be letter, underscore, or non-ASCII (>= 0x80)
|
// First pass: find the end of the class name and check if there are escape sequences
|
||||||
// Can also be hyphen if not followed by digit or another hyphen
|
var i: usize = 0;
|
||||||
const first = input[0];
|
var has_escape = false;
|
||||||
if (first == '-') {
|
var first_char_validated = false;
|
||||||
if (input.len < 2) {
|
|
||||||
|
while (i < input.len) {
|
||||||
|
const b = input[i];
|
||||||
|
|
||||||
|
if (b == '\\') {
|
||||||
|
// Escape sequence
|
||||||
|
if (i + 1 >= input.len) {
|
||||||
@branchHint(.cold);
|
@branchHint(.cold);
|
||||||
return error.InvalidClassSelector;
|
return error.InvalidClassSelector;
|
||||||
}
|
}
|
||||||
const second = input[1];
|
has_escape = true;
|
||||||
|
i += 2; // Skip backslash and escaped char
|
||||||
|
first_char_validated = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate first character if not yet validated
|
||||||
|
if (!first_char_validated) {
|
||||||
|
if (b == '-') {
|
||||||
|
if (i + 1 >= input.len) {
|
||||||
|
@branchHint(.cold);
|
||||||
|
return error.InvalidClassSelector;
|
||||||
|
}
|
||||||
|
const second = input[i + 1];
|
||||||
if (second == '-' or std.ascii.isDigit(second)) {
|
if (second == '-' or std.ascii.isDigit(second)) {
|
||||||
@branchHint(.cold);
|
@branchHint(.cold);
|
||||||
return error.InvalidClassSelector;
|
return error.InvalidClassSelector;
|
||||||
}
|
}
|
||||||
} else if (!std.ascii.isAlphabetic(first) and first != '_' and first < 0x80) {
|
} else if (!std.ascii.isAlphabetic(b) and b != '_' and b < 0x80) {
|
||||||
@branchHint(.cold);
|
@branchHint(.cold);
|
||||||
return error.InvalidClassSelector;
|
return error.InvalidClassSelector;
|
||||||
}
|
}
|
||||||
|
first_char_validated = true;
|
||||||
|
}
|
||||||
|
|
||||||
var i: usize = 1;
|
// Check if this is a valid class name character
|
||||||
for (input[1..]) |b| {
|
|
||||||
switch (b) {
|
switch (b) {
|
||||||
'a'...'z', 'A'...'Z', '0'...'9', '-', '_' => {},
|
'a'...'z', 'A'...'Z', '0'...'9', '-', '_' => {},
|
||||||
0x80...0xFF => {}, // non-ASCII characters
|
0x80...0xFF => {}, // non-ASCII characters
|
||||||
@@ -751,8 +819,36 @@ fn class(self: *Parser) ![]const u8 {
|
|||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
@branchHint(.cold);
|
||||||
|
return error.InvalidClassSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
const raw = input[0..i];
|
||||||
self.input = input[i..];
|
self.input = input[i..];
|
||||||
return input[0..i];
|
|
||||||
|
// If no escape sequences, return the slice as-is
|
||||||
|
if (!has_escape) {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build unescaped string
|
||||||
|
var result = try std.ArrayList(u8).initCapacity(arena, raw.len);
|
||||||
|
var j: usize = 0;
|
||||||
|
while (j < raw.len) {
|
||||||
|
if (raw[j] == '\\') {
|
||||||
|
j += 1; // Skip backslash
|
||||||
|
if (j < raw.len) {
|
||||||
|
try result.append(arena, raw[j]); // Add escaped char
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try result.append(arena, raw[j]);
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tag(self: *Parser) ![]const u8 {
|
fn tag(self: *Parser) ![]const u8 {
|
||||||
@@ -941,227 +1037,231 @@ fn fastEql(a: []const u8, comptime b: []const u8) bool {
|
|||||||
|
|
||||||
const testing = @import("../../../testing.zig");
|
const testing = @import("../../../testing.zig");
|
||||||
test "Selector: Parser.ID" {
|
test "Selector: Parser.ID" {
|
||||||
|
const arena = testing.allocator;
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#" };
|
var parser = Parser{ .input = "#" };
|
||||||
try testing.expectError(error.InvalidIDSelector, parser.id());
|
try testing.expectError(error.InvalidIDSelector, parser.id(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "# " };
|
var parser = Parser{ .input = "# " };
|
||||||
try testing.expectError(error.InvalidIDSelector, parser.id());
|
try testing.expectError(error.InvalidIDSelector, parser.id(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#1" };
|
var parser = Parser{ .input = "#1" };
|
||||||
try testing.expectError(error.InvalidIDSelector, parser.id());
|
try testing.expectError(error.InvalidIDSelector, parser.id(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#9abc" };
|
var parser = Parser{ .input = "#9abc" };
|
||||||
try testing.expectError(error.InvalidIDSelector, parser.id());
|
try testing.expectError(error.InvalidIDSelector, parser.id(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#-1" };
|
var parser = Parser{ .input = "#-1" };
|
||||||
try testing.expectError(error.InvalidIDSelector, parser.id());
|
try testing.expectError(error.InvalidIDSelector, parser.id(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#-5abc" };
|
var parser = Parser{ .input = "#-5abc" };
|
||||||
try testing.expectError(error.InvalidIDSelector, parser.id());
|
try testing.expectError(error.InvalidIDSelector, parser.id(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#--" };
|
var parser = Parser{ .input = "#--" };
|
||||||
try testing.expectError(error.InvalidIDSelector, parser.id());
|
try testing.expectError(error.InvalidIDSelector, parser.id(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#--test" };
|
var parser = Parser{ .input = "#--test" };
|
||||||
try testing.expectError(error.InvalidIDSelector, parser.id());
|
try testing.expectError(error.InvalidIDSelector, parser.id(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#-" };
|
var parser = Parser{ .input = "#-" };
|
||||||
try testing.expectError(error.InvalidIDSelector, parser.id());
|
try testing.expectError(error.InvalidIDSelector, parser.id(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#over" };
|
var parser = Parser{ .input = "#over" };
|
||||||
try testing.expectEqual("over", try parser.id());
|
try testing.expectEqual("over", try parser.id(arena));
|
||||||
try testing.expectEqual("", parser.input);
|
try testing.expectEqual("", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#myID123" };
|
var parser = Parser{ .input = "#myID123" };
|
||||||
try testing.expectEqual("myID123", try parser.id());
|
try testing.expectEqual("myID123", try parser.id(arena));
|
||||||
try testing.expectEqual("", parser.input);
|
try testing.expectEqual("", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#_test" };
|
var parser = Parser{ .input = "#_test" };
|
||||||
try testing.expectEqual("_test", try parser.id());
|
try testing.expectEqual("_test", try parser.id(arena));
|
||||||
try testing.expectEqual("", parser.input);
|
try testing.expectEqual("", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#test_123" };
|
var parser = Parser{ .input = "#test_123" };
|
||||||
try testing.expectEqual("test_123", try parser.id());
|
try testing.expectEqual("test_123", try parser.id(arena));
|
||||||
try testing.expectEqual("", parser.input);
|
try testing.expectEqual("", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#-test" };
|
var parser = Parser{ .input = "#-test" };
|
||||||
try testing.expectEqual("-test", try parser.id());
|
try testing.expectEqual("-test", try parser.id(arena));
|
||||||
try testing.expectEqual("", parser.input);
|
try testing.expectEqual("", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#my-id" };
|
var parser = Parser{ .input = "#my-id" };
|
||||||
try testing.expectEqual("my-id", try parser.id());
|
try testing.expectEqual("my-id", try parser.id(arena));
|
||||||
try testing.expectEqual("", parser.input);
|
try testing.expectEqual("", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#test other" };
|
var parser = Parser{ .input = "#test other" };
|
||||||
try testing.expectEqual("test", try parser.id());
|
try testing.expectEqual("test", try parser.id(arena));
|
||||||
try testing.expectEqual(" other", parser.input);
|
try testing.expectEqual(" other", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#id.class" };
|
var parser = Parser{ .input = "#id.class" };
|
||||||
try testing.expectEqual("id", try parser.id());
|
try testing.expectEqual("id", try parser.id(arena));
|
||||||
try testing.expectEqual(".class", parser.input);
|
try testing.expectEqual(".class", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#id:hover" };
|
var parser = Parser{ .input = "#id:hover" };
|
||||||
try testing.expectEqual("id", try parser.id());
|
try testing.expectEqual("id", try parser.id(arena));
|
||||||
try testing.expectEqual(":hover", parser.input);
|
try testing.expectEqual(":hover", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#id>child" };
|
var parser = Parser{ .input = "#id>child" };
|
||||||
try testing.expectEqual("id", try parser.id());
|
try testing.expectEqual("id", try parser.id(arena));
|
||||||
try testing.expectEqual(">child", parser.input);
|
try testing.expectEqual(">child", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "#id[attr]" };
|
var parser = Parser{ .input = "#id[attr]" };
|
||||||
try testing.expectEqual("id", try parser.id());
|
try testing.expectEqual("id", try parser.id(arena));
|
||||||
try testing.expectEqual("[attr]", parser.input);
|
try testing.expectEqual("[attr]", parser.input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test "Selector: Parser.class" {
|
test "Selector: Parser.class" {
|
||||||
|
const arena = testing.allocator;
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "." };
|
var parser = Parser{ .input = "." };
|
||||||
try testing.expectError(error.InvalidClassSelector, parser.class());
|
try testing.expectError(error.InvalidClassSelector, parser.class(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ". " };
|
var parser = Parser{ .input = ". " };
|
||||||
try testing.expectError(error.InvalidClassSelector, parser.class());
|
try testing.expectError(error.InvalidClassSelector, parser.class(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".1" };
|
var parser = Parser{ .input = ".1" };
|
||||||
try testing.expectError(error.InvalidClassSelector, parser.class());
|
try testing.expectError(error.InvalidClassSelector, parser.class(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".9abc" };
|
var parser = Parser{ .input = ".9abc" };
|
||||||
try testing.expectError(error.InvalidClassSelector, parser.class());
|
try testing.expectError(error.InvalidClassSelector, parser.class(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".-1" };
|
var parser = Parser{ .input = ".-1" };
|
||||||
try testing.expectError(error.InvalidClassSelector, parser.class());
|
try testing.expectError(error.InvalidClassSelector, parser.class(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".-5abc" };
|
var parser = Parser{ .input = ".-5abc" };
|
||||||
try testing.expectError(error.InvalidClassSelector, parser.class());
|
try testing.expectError(error.InvalidClassSelector, parser.class(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".--" };
|
var parser = Parser{ .input = ".--" };
|
||||||
try testing.expectError(error.InvalidClassSelector, parser.class());
|
try testing.expectError(error.InvalidClassSelector, parser.class(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".--test" };
|
var parser = Parser{ .input = ".--test" };
|
||||||
try testing.expectError(error.InvalidClassSelector, parser.class());
|
try testing.expectError(error.InvalidClassSelector, parser.class(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".-" };
|
var parser = Parser{ .input = ".-" };
|
||||||
try testing.expectError(error.InvalidClassSelector, parser.class());
|
try testing.expectError(error.InvalidClassSelector, parser.class(arena));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".active" };
|
var parser = Parser{ .input = ".active" };
|
||||||
try testing.expectEqual("active", try parser.class());
|
try testing.expectEqual("active", try parser.class(arena));
|
||||||
try testing.expectEqual("", parser.input);
|
try testing.expectEqual("", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".myClass123" };
|
var parser = Parser{ .input = ".myClass123" };
|
||||||
try testing.expectEqual("myClass123", try parser.class());
|
try testing.expectEqual("myClass123", try parser.class(arena));
|
||||||
try testing.expectEqual("", parser.input);
|
try testing.expectEqual("", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = "._test" };
|
var parser = Parser{ .input = "._test" };
|
||||||
try testing.expectEqual("_test", try parser.class());
|
try testing.expectEqual("_test", try parser.class(arena));
|
||||||
try testing.expectEqual("", parser.input);
|
try testing.expectEqual("", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".test_123" };
|
var parser = Parser{ .input = ".test_123" };
|
||||||
try testing.expectEqual("test_123", try parser.class());
|
try testing.expectEqual("test_123", try parser.class(arena));
|
||||||
try testing.expectEqual("", parser.input);
|
try testing.expectEqual("", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".-test" };
|
var parser = Parser{ .input = ".-test" };
|
||||||
try testing.expectEqual("-test", try parser.class());
|
try testing.expectEqual("-test", try parser.class(arena));
|
||||||
try testing.expectEqual("", parser.input);
|
try testing.expectEqual("", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".my-class" };
|
var parser = Parser{ .input = ".my-class" };
|
||||||
try testing.expectEqual("my-class", try parser.class());
|
try testing.expectEqual("my-class", try parser.class(arena));
|
||||||
try testing.expectEqual("", parser.input);
|
try testing.expectEqual("", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".test other" };
|
var parser = Parser{ .input = ".test other" };
|
||||||
try testing.expectEqual("test", try parser.class());
|
try testing.expectEqual("test", try parser.class(arena));
|
||||||
try testing.expectEqual(" other", parser.input);
|
try testing.expectEqual(" other", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".class1.class2" };
|
var parser = Parser{ .input = ".class1.class2" };
|
||||||
try testing.expectEqual("class1", try parser.class());
|
try testing.expectEqual("class1", try parser.class(arena));
|
||||||
try testing.expectEqual(".class2", parser.input);
|
try testing.expectEqual(".class2", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".class:hover" };
|
var parser = Parser{ .input = ".class:hover" };
|
||||||
try testing.expectEqual("class", try parser.class());
|
try testing.expectEqual("class", try parser.class(arena));
|
||||||
try testing.expectEqual(":hover", parser.input);
|
try testing.expectEqual(":hover", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".class>child" };
|
var parser = Parser{ .input = ".class>child" };
|
||||||
try testing.expectEqual("class", try parser.class());
|
try testing.expectEqual("class", try parser.class(arena));
|
||||||
try testing.expectEqual(">child", parser.input);
|
try testing.expectEqual(">child", parser.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var parser = Parser{ .input = ".class[attr]" };
|
var parser = Parser{ .input = ".class[attr]" };
|
||||||
try testing.expectEqual("class", try parser.class());
|
try testing.expectEqual("class", try parser.class(arena));
|
||||||
try testing.expectEqual("[attr]", parser.input);
|
try testing.expectEqual("[attr]", parser.input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1354,3 +1454,4 @@ test "Selector: Parser.parseNthPattern" {
|
|||||||
try testing.expectEqual(" )", parser.input);
|
try testing.expectEqual(" )", parser.input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ pub const Jar = struct {
|
|||||||
|
|
||||||
pub fn populateFromResponse(self: *Jar, uri: *const Uri, set_cookie: []const u8) !void {
|
pub fn populateFromResponse(self: *Jar, uri: *const Uri, set_cookie: []const u8) !void {
|
||||||
const c = Cookie.parse(self.allocator, uri, set_cookie) catch |err| {
|
const c = Cookie.parse(self.allocator, uri, set_cookie) catch |err| {
|
||||||
log.warn(.web_api, "cookie parse failed", .{ .raw = set_cookie, .err = err });
|
log.warn(.page, "cookie parse failed", .{ .raw = set_cookie, .err = err });
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -312,7 +312,7 @@ pub const Cookie = struct {
|
|||||||
// Algolia, for example, will call document.setCookie with
|
// Algolia, for example, will call document.setCookie with
|
||||||
// an expired value which is literally 'Invalid Date'
|
// an expired value which is literally 'Invalid Date'
|
||||||
// (it's trying to do something like: `new Date() + undefined`).
|
// (it's trying to do something like: `new Date() + undefined`).
|
||||||
log.debug(.web_api, "cookie expires date", .{ .date = expires_ });
|
log.debug(.page, "cookie expires date", .{ .date = expires_ });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,8 +88,8 @@ pub fn LogInterceptor(comptime BC: type) type {
|
|||||||
self.bc.cdp.sendEvent("Log.entryAdded", .{
|
self.bc.cdp.sendEvent("Log.entryAdded", .{
|
||||||
.entry = .{
|
.entry = .{
|
||||||
.source = switch (scope) {
|
.source = switch (scope) {
|
||||||
.js, .user_script, .console, .web_api, .script_event => "javascript",
|
.js, .console => "javascript",
|
||||||
.http, .fetch, .xhr => "network",
|
.http => "network",
|
||||||
.telemetry, .unknown_prop, .interceptor => unreachable, // filtered out in writer above
|
.telemetry, .unknown_prop, .interceptor => unreachable, // filtered out in writer above
|
||||||
else => "other",
|
else => "other",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const Allocator = std.mem.Allocator;
|
|||||||
|
|
||||||
pub const FetchOpts = struct {
|
pub const FetchOpts = struct {
|
||||||
wait_ms: u32 = 5000,
|
wait_ms: u32 = 5000,
|
||||||
dump: dump.Opts,
|
dump: dump.RootOpts,
|
||||||
writer: ?*std.Io.Writer = null,
|
writer: ?*std.Io.Writer = null,
|
||||||
};
|
};
|
||||||
pub fn fetch(app: *App, url: [:0]const u8, opts: FetchOpts) !void {
|
pub fn fetch(app: *App, url: [:0]const u8, opts: FetchOpts) !void {
|
||||||
@@ -64,7 +64,7 @@ pub fn fetch(app: *App, url: [:0]const u8, opts: FetchOpts) !void {
|
|||||||
_ = session.fetchWait(opts.wait_ms);
|
_ = session.fetchWait(opts.wait_ms);
|
||||||
|
|
||||||
const writer = opts.writer orelse return;
|
const writer = opts.writer orelse return;
|
||||||
try dump.deep(page.document.asNode(), opts.dump, writer);
|
try dump.root(opts.dump, writer, page);
|
||||||
try writer.flush();
|
try writer.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
src/log.zig
13
src/log.zig
@@ -33,19 +33,12 @@ pub const Scope = enum {
|
|||||||
http,
|
http,
|
||||||
page,
|
page,
|
||||||
js,
|
js,
|
||||||
loop,
|
|
||||||
event,
|
event,
|
||||||
scheduler,
|
scheduler,
|
||||||
not_implemented,
|
not_implemented,
|
||||||
script_event,
|
|
||||||
telemetry,
|
telemetry,
|
||||||
user_script,
|
|
||||||
unknown_prop,
|
|
||||||
web_api,
|
|
||||||
xhr,
|
|
||||||
fetch,
|
|
||||||
polyfill,
|
|
||||||
interceptor,
|
interceptor,
|
||||||
|
unknown_prop,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Opts = struct {
|
const Opts = struct {
|
||||||
@@ -394,7 +387,7 @@ test "log: data" {
|
|||||||
const string = try testing.allocator.dupe(u8, "spice_must_flow");
|
const string = try testing.allocator.dupe(u8, "spice_must_flow");
|
||||||
defer testing.allocator.free(string);
|
defer testing.allocator.free(string);
|
||||||
|
|
||||||
try logTo(.http, .warn, "a msg", .{
|
try logTo(.page, .warn, "a msg", .{
|
||||||
.cint = 5,
|
.cint = 5,
|
||||||
.cfloat = 3.43,
|
.cfloat = 3.43,
|
||||||
.int = @as(i16, -49),
|
.int = @as(i16, -49),
|
||||||
@@ -409,7 +402,7 @@ test "log: data" {
|
|||||||
.level = Level.warn,
|
.level = Level.warn,
|
||||||
}, &aw.writer);
|
}, &aw.writer);
|
||||||
|
|
||||||
try testing.expectEqual("$time=1739795092929 $scope=http $level=warn $msg=\"a msg\" " ++
|
try testing.expectEqual("$time=1739795092929 $scope=page $level=warn $msg=\"a msg\" " ++
|
||||||
"cint=5 cfloat=3.43 int=-49 float=0.0003232 bt=true bf=false " ++
|
"cint=5 cfloat=3.43 int=-49 float=0.0003232 bt=true bf=false " ++
|
||||||
"nn=33 n=null lit=over9000! slice=spice_must_flow " ++
|
"nn=33 n=null lit=over9000! slice=spice_must_flow " ++
|
||||||
"err=Nope level=warn\n", aw.written());
|
"err=Nope level=warn\n", aw.written());
|
||||||
|
|||||||
22
src/main.zig
22
src/main.zig
@@ -125,8 +125,8 @@ fn run(allocator: Allocator, main_arena: Allocator) !void {
|
|||||||
var fetch_opts = lp.FetchOpts{
|
var fetch_opts = lp.FetchOpts{
|
||||||
.wait_ms = 5000,
|
.wait_ms = 5000,
|
||||||
.dump = .{
|
.dump = .{
|
||||||
|
.strip = opts.strip,
|
||||||
.with_base = opts.withbase,
|
.with_base = opts.withbase,
|
||||||
.strip_mode = opts.strip_mode,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -245,7 +245,7 @@ const Command = struct {
|
|||||||
dump: bool = false,
|
dump: bool = false,
|
||||||
common: Common,
|
common: Common,
|
||||||
withbase: bool = false,
|
withbase: bool = false,
|
||||||
strip_mode: lp.dump.Opts.StripMode = .{},
|
strip: lp.dump.Opts.Strip = .{},
|
||||||
};
|
};
|
||||||
|
|
||||||
const Common = struct {
|
const Common = struct {
|
||||||
@@ -511,7 +511,7 @@ fn parseFetchArgs(
|
|||||||
var withbase: bool = false;
|
var withbase: bool = false;
|
||||||
var url: ?[:0]const u8 = null;
|
var url: ?[:0]const u8 = null;
|
||||||
var common: Command.Common = .{};
|
var common: Command.Common = .{};
|
||||||
var strip_mode: lp.dump.Opts.StripMode = .{};
|
var strip: lp.dump.Opts.Strip = .{};
|
||||||
|
|
||||||
while (args.next()) |opt| {
|
while (args.next()) |opt| {
|
||||||
if (std.mem.eql(u8, "--dump", opt)) {
|
if (std.mem.eql(u8, "--dump", opt)) {
|
||||||
@@ -524,7 +524,7 @@ fn parseFetchArgs(
|
|||||||
.feature = "--noscript argument",
|
.feature = "--noscript argument",
|
||||||
.hint = "use '--strip_mode js' instead",
|
.hint = "use '--strip_mode js' instead",
|
||||||
});
|
});
|
||||||
strip_mode.js = true;
|
strip.js = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -543,15 +543,15 @@ fn parseFetchArgs(
|
|||||||
while (it.next()) |part| {
|
while (it.next()) |part| {
|
||||||
const trimmed = std.mem.trim(u8, part, &std.ascii.whitespace);
|
const trimmed = std.mem.trim(u8, part, &std.ascii.whitespace);
|
||||||
if (std.mem.eql(u8, trimmed, "js")) {
|
if (std.mem.eql(u8, trimmed, "js")) {
|
||||||
strip_mode.js = true;
|
strip.js = true;
|
||||||
} else if (std.mem.eql(u8, trimmed, "ui")) {
|
} else if (std.mem.eql(u8, trimmed, "ui")) {
|
||||||
strip_mode.ui = true;
|
strip.ui = true;
|
||||||
} else if (std.mem.eql(u8, trimmed, "css")) {
|
} else if (std.mem.eql(u8, trimmed, "css")) {
|
||||||
strip_mode.css = true;
|
strip.css = true;
|
||||||
} else if (std.mem.eql(u8, trimmed, "full")) {
|
} else if (std.mem.eql(u8, trimmed, "full")) {
|
||||||
strip_mode.js = true;
|
strip.js = true;
|
||||||
strip_mode.ui = true;
|
strip.ui = true;
|
||||||
strip_mode.css = true;
|
strip.css = true;
|
||||||
} else {
|
} else {
|
||||||
log.fatal(.app, "invalid option choice", .{ .arg = "--strip_mode", .value = trimmed });
|
log.fatal(.app, "invalid option choice", .{ .arg = "--strip_mode", .value = trimmed });
|
||||||
}
|
}
|
||||||
@@ -583,9 +583,9 @@ fn parseFetchArgs(
|
|||||||
return .{
|
return .{
|
||||||
.url = url.?,
|
.url = url.?,
|
||||||
.dump = dump,
|
.dump = dump,
|
||||||
|
.strip = strip,
|
||||||
.common = common,
|
.common = common,
|
||||||
.withbase = withbase,
|
.withbase = withbase,
|
||||||
.strip_mode = strip_mode,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user