Fix typos

Encode unicode nonbreaking space
This commit is contained in:
Karl Seguin
2025-12-05 17:48:49 +08:00
parent dd3781a1ea
commit 61a1a2564e
7 changed files with 68 additions and 46 deletions

View File

@@ -486,7 +486,7 @@ pub const Client = struct {
}
// called by CDP
// Websocket frames have a variable lenght header. For server-client,
// Websocket frames have a variable length header. For server-client,
// it could be anywhere from 2 to 10 bytes. Our IO.Loop doesn't have
// writev, so we need to get creative. We'll JSON serialize to a
// buffer, where the first 10 bytes are reserved. We can then backfill

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
@@ -74,7 +74,11 @@ fn _deep(node: *Node, opts: Opts, comptime force_slot: bool, writer: *std.Io.Wri
try writer.writeAll(cd.getData());
try writer.writeAll("-->");
} else {
try writeEscapedText(cd.getData(), writer);
if (shouldEscapeText(node._parent)) {
try writeEscapedText(cd.getData(), writer);
} else {
try writer.writeAll(cd.getData());
}
}
},
.element => |el| {
@@ -245,34 +249,46 @@ fn shouldStripElement(el: *const Node.Element, opts: Opts) bool {
return false;
}
fn shouldEscapeText(node_: ?*Node) bool {
const node = node_ orelse return true;
if (node.is(Node.Element.Html.Script) != null) {
return false;
}
return true;
}
fn writeEscapedText(text: []const u8, writer: *std.Io.Writer) !void {
// Fast path: if no special characters, write directly
const first_special = std.mem.indexOfAny(u8, text, "&<>") orelse {
const first_special = std.mem.indexOfAnyPos(u8, text, 0, &.{ '&', '<', '>', 194 }) orelse {
return writer.writeAll(text);
};
try writer.writeAll(text[0..first_special]);
try writer.writeAll(switch (text[first_special]) {
'&' => "&amp;",
'<' => "&lt;",
'>' => "&gt;",
else => unreachable,
});
var remaining = try writeEscapedByte(text, first_special, writer);
// Process remaining text
var remaining = text[first_special + 1 ..];
while (std.mem.indexOfAny(u8, remaining, "&<>")) |offset| {
while (std.mem.indexOfAnyPos(u8, remaining, 0, &.{ '&', '<', '>', 194 })) |offset| {
try writer.writeAll(remaining[0..offset]);
try writer.writeAll(switch (remaining[offset]) {
'&' => "&amp;",
'<' => "&lt;",
'>' => "&gt;",
else => unreachable,
});
remaining = remaining[offset + 1 ..];
remaining = try writeEscapedByte(remaining, offset, writer);
}
if (remaining.len > 0) {
try writer.writeAll(remaining);
}
}
fn writeEscapedByte(input: []const u8, index: usize, writer: *std.Io.Writer) ![]const u8 {
switch (input[index]) {
'&' => try writer.writeAll("&amp;"),
'<' => try writer.writeAll("&lt;"),
'>' => try writer.writeAll("&gt;"),
194 => {
// non breaking space
if (input.len > index + 1 and input[index + 1] == 160) {
try writer.writeAll("&nbsp;");
return input [index + 2 ..];
}
try writer.writeByte(194);
},
else => unreachable,
}
return input[index + 1..];
}

View File

@@ -164,7 +164,17 @@
if (observed_ids[script_id] === 'fail') {
return;
}
observed_ids[script_id] = status;
if (document.currentScript != null) {
if (document.currentScript.onerror === null) {
document.currentScript.onerror = function() {
observed_ids[document.currentScript.id] = 'fail';
failed = true;
}
}
}
}
function _currentScriptId() {
@@ -201,17 +211,15 @@
return `array: \n${value.map(_displayValue).join('\n')}\n`;
}
// Quickjs can deal with cyclical objects, but browsers can't. We
// serialize with a custom replacer so that the tests can be run in browsers.
const seen = [];
return JSON.stringify(value, function(key, val) {
if (val != null && typeof val == "object") {
if (seen.indexOf(val) >= 0) {
return;
}
seen.push(val);
}
return val;
});
if (val != null && typeof val == "object") {
if (seen.indexOf(val) >= 0) {
return;
}
seen.push(val);
}
return val;
});
}
})();

View File

@@ -336,13 +336,16 @@ pub fn setAttributeSafe(self: *Element, name: []const u8, value: []const u8, pag
_ = try attributes.putSafe(name, value, self, page);
}
fn getOrCreateAttributeList(self: *Element, page: *Page) !*Attribute.List {
return self._attributes orelse {
const a = try page.arena.create(Attribute.List);
a.* = .{};
self._attributes = a;
return a;
};
pub fn getOrCreateAttributeList(self: *Element, page: *Page) !*Attribute.List {
return self._attributes orelse return self.createAttributeList(page);
}
pub fn createAttributeList(self: *Element, page: *Page) !*Attribute.List {
std.debug.assert(self._attributes == null);
const a = try page.arena.create(Attribute.List);
a.* = .{.normalize = self._namespace == .html};
self._attributes = a;
return a;
}
pub fn getShadowRoot(self: *Element, page: *Page) ?*ShadowRoot {
@@ -370,12 +373,7 @@ pub fn setAttributeNode(self: *Element, attr: *Attribute, page: *Page) !?*Attrib
_ = try el.removeAttributeNode(attr, page);
}
const attributes = self._attributes orelse blk: {
const a = try page.arena.create(Attribute.List);
a.* = .{};
self._attributes = a;
break :blk a;
};
const attributes = try self.getOrCreateAttributeList(page);
return attributes.putAttribute(attr, self, page);
}

View File

@@ -105,7 +105,7 @@ pub const JsApi = struct {
pub const src = bridge.accessor(Script.getSrc, Script.setSrc, .{});
pub const @"type" = bridge.accessor(Script.getType, Script.setType, .{});
pub const onload = bridge.accessor(Script.getOnLoad, Script.setOnLoad, .{});
pub const onerorr = bridge.accessor(Script.getOnError, Script.setOnError, .{});
pub const onerror = bridge.accessor(Script.getOnError, Script.setOnError, .{});
pub const noModule = bridge.accessor(Script.getNoModule, null, .{});
};

View File

@@ -724,7 +724,7 @@ const IsolatedWorld = struct {
// The isolate world must share at least some of the state with the related page, specifically the DocumentHTML
// (assuming grantUniveralAccess will be set to True!).
// We just created the world and the page. The page's state lives in the session, but is update on navigation.
// This also means this pointer becomes invalid after removePage untill a new page is created.
// This also means this pointer becomes invalid after removePage until a new page is created.
// Currently we have only 1 page/frame and thus also only 1 state in the isolate world.
pub fn createContext(self: *IsolatedWorld, page: *Page) !void {
// if (self.executor.context != null) return error.Only1IsolatedContextSupported;

View File

@@ -821,7 +821,7 @@ pub const Transfer = struct {
self.deinit();
}
// abortAuthChallenge is called when an auth chanllenge interception is
// abortAuthChallenge is called when an auth challenge interception is
// abort. We don't call self.client.endTransfer here b/c it has been done
// before interception process.
pub fn abortAuthChallenge(self: *Transfer) void {