mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-16 08:18:59 +00:00
preserve casing (tags/attributes) for SVG/XML/MathML namespace
This commit is contained in:
@@ -355,7 +355,7 @@ pub fn documentIsLoaded(self: *Page) void {
|
||||
}
|
||||
|
||||
pub fn _documentIsLoaded(self: *Page) !void {
|
||||
const event = try Event.init("DOMContentLoaded", .{}, self);
|
||||
const event = try Event.init("DOMContentLoaded", .{ .bubbles = true }, self);
|
||||
try self._event_manager.dispatch(
|
||||
self.document.asEventTarget(),
|
||||
event,
|
||||
@@ -1292,7 +1292,9 @@ fn populateElementAttributes(self: *Page, element: *Element, list: anytype) !voi
|
||||
var existing = list orelse return;
|
||||
|
||||
var attributes = try self.arena.create(Element.Attribute.List);
|
||||
attributes.* = .{};
|
||||
attributes.* = .{
|
||||
.normalize = existing.normalize,
|
||||
};
|
||||
|
||||
var it = existing.iterator();
|
||||
while (it.next()) |attr| {
|
||||
@@ -1306,12 +1308,10 @@ fn populateElementAttributes(self: *Page, element: *Element, list: anytype) !voi
|
||||
if (@TypeOf(list) == @TypeOf(null) or list.count() == 0) {
|
||||
return;
|
||||
}
|
||||
var attributes = try self.arena.create(Element.Attribute.List);
|
||||
attributes.* = .{};
|
||||
var attributes = try element.createAttributeList(self);
|
||||
while (list.next()) |attr| {
|
||||
try attributes.putNew(attr.name.local.slice(), attr.value.slice(), self);
|
||||
}
|
||||
element._attributes = attributes;
|
||||
}
|
||||
|
||||
pub fn createTextNode(self: *Page, text: []const u8) !*Node {
|
||||
|
||||
@@ -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>
|
||||
@@ -284,11 +284,11 @@ fn writeEscapedByte(input: []const u8, index: usize, writer: *std.Io.Writer) ![]
|
||||
// non breaking space
|
||||
if (input.len > index + 1 and input[index + 1] == 160) {
|
||||
try writer.writeAll(" ");
|
||||
return input [index + 2 ..];
|
||||
return input[index + 2 ..];
|
||||
}
|
||||
try writer.writeByte(194);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
return input[index + 1..];
|
||||
return input[index + 1 ..];
|
||||
}
|
||||
|
||||
@@ -242,13 +242,7 @@ fn _addAttrsIfMissingCallback(self: *Parser, node: *Node, attributes: h5e.Attrib
|
||||
const element = node.as(Element);
|
||||
const page = self.page;
|
||||
|
||||
const attr_list = element._attributes orelse blk: {
|
||||
const a = try page.arena.create(@import("../webapi/element/Attribute.zig").List);
|
||||
a.* = .{};
|
||||
element._attributes = a;
|
||||
break :blk a;
|
||||
};
|
||||
|
||||
const attr_list = try element.getOrCreateAttributeList(page);
|
||||
while (attributes.next()) |attr| {
|
||||
const name = attr.name.local.slice();
|
||||
const value = attr.value.slice();
|
||||
|
||||
@@ -52,3 +52,16 @@
|
||||
testing.expectEqual(0, ec.length);
|
||||
testing.expectEqual(undefined, ec[0]);
|
||||
</script>
|
||||
|
||||
<script id=nonBreakingSpace>
|
||||
// Test non-breaking space encoding (critical for React hydration)
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = 'hello\xa0world';
|
||||
testing.expectEqual('hello\xa0world', div.textContent);
|
||||
testing.expectEqual('hello world', div.innerHTML);
|
||||
|
||||
// Test that outerHTML also encodes non-breaking spaces correctly
|
||||
const p = document.createElement('p');
|
||||
p.textContent = 'XAnge\xa0Privacy';
|
||||
testing.expectEqual('<p>XAnge Privacy</p>', p.outerHTML);
|
||||
</script>
|
||||
|
||||
@@ -6,23 +6,61 @@
|
||||
<SVG id=svg3></SVG>
|
||||
|
||||
<script id=svg>
|
||||
let svg1 = $('#svg1');
|
||||
testing.expectEqual('svg', svg1.tagName);
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', svg1.namespaceURI);
|
||||
{
|
||||
let svg1 = $('#svg1');
|
||||
testing.expectEqual('svg', svg1.tagName);
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', svg1.namespaceURI);
|
||||
|
||||
let svg2 = $('#svg2');
|
||||
testing.expectEqual('svg', svg2.tagName);
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', svg2.namespaceURI);
|
||||
let svg2 = $('#svg2');
|
||||
testing.expectEqual('svg', svg2.tagName);
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', svg2.namespaceURI);
|
||||
|
||||
let svg3 = $('#svg3');
|
||||
testing.expectEqual('svg', svg3.tagName);
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', svg3.namespaceURI);
|
||||
let svg3 = $('#svg3');
|
||||
testing.expectEqual('svg', svg3.tagName);
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', svg3.namespaceURI);
|
||||
|
||||
const svg4 = document.createElementNS('http://www.w3.org/2000/svg', 'SvG');
|
||||
testing.expectEqual('SvG', svg4.tagName);
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', svg4.namespaceURI);
|
||||
const svg4 = document.createElementNS('http://www.w3.org/2000/svg', 'SvG');
|
||||
testing.expectEqual('SvG', svg4.tagName);
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', svg4.namespaceURI);
|
||||
|
||||
const svg5 = document.createElement('SvG');
|
||||
testing.expectEqual('SVG', svg5.tagName);
|
||||
testing.expectEqual('http://www.w3.org/1999/xhtml', svg5.namespaceURI);
|
||||
const svg5 = document.createElement('SvG');
|
||||
testing.expectEqual('SVG', svg5.tagName);
|
||||
testing.expectEqual('http://www.w3.org/1999/xhtml', svg5.namespaceURI);
|
||||
}
|
||||
</script>
|
||||
|
||||
<svg id=lower width="200" height="100" style="border:1px solid #ccc" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100">
|
||||
<rect></rect>
|
||||
<text x="100" y="95" font-size="14" text-anchor="middle">OVER 9000!!</text>
|
||||
</svg>
|
||||
|
||||
<SVG ID=UPPER WIDTH="200" HEIGHT="100" STYLE="BORDER:1PX SOLID #CCC" XMLNS="http://www.w3.org/2000/svg" VIEWBOX="0 0 200 100">
|
||||
<RECT></RECT>
|
||||
<TEXT X="100" Y="95" FONT-SIZE="14" TEXT-ANCHOR="MIDDLE">OVER 9000!!!</TEXT>
|
||||
</SVG>
|
||||
|
||||
<script id=casing>
|
||||
testing.expectEqual(false, 'AString' instanceof SVGElement);
|
||||
|
||||
const lower = $('#lower');
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', lower.getAttribute('xmlns'));
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', lower.getAttributeNode('xmlns').value);
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', lower.attributes.getNamedItem('xmlns').value);
|
||||
testing.expectEqual('0 0 200 100', lower.getAttribute('viewBox'));
|
||||
testing.expectEqual('viewBox', lower.getAttributeNode('viewBox').name);
|
||||
testing.expectEqual(true, lower.outerHTML.includes('viewBox'));
|
||||
testing.expectEqual('svg', lower.tagName);
|
||||
testing.expectEqual('rect', lower.querySelector('rect').tagName);
|
||||
testing.expectEqual('text', lower.querySelector('text').tagName);
|
||||
|
||||
const upper = $('#UPPER');
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', upper.getAttribute('xmlns'));
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', upper.getAttributeNode('xmlns').value);
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', upper.attributes.getNamedItem('xmlns').value);
|
||||
testing.expectEqual('0 0 200 100', upper.getAttribute('viewBox'));
|
||||
testing.expectEqual('viewBox', upper.getAttributeNode('viewBox').name);
|
||||
testing.expectEqual(true, upper.outerHTML.includes('viewBox'));
|
||||
testing.expectEqual('svg', upper.tagName);
|
||||
testing.expectEqual('rect', upper.querySelector('rect').tagName);
|
||||
testing.expectEqual('text', upper.querySelector('text').tagName);
|
||||
</script>
|
||||
|
||||
@@ -28,3 +28,22 @@
|
||||
testing.expectEqual('a<p></p>b', container.innerHTML);
|
||||
testing.expectEqual(3, container.childNodes.length);
|
||||
</script>
|
||||
|
||||
<span id=token class="token" style="color:#ce9178">"puppeteer "</span>
|
||||
<h3 id=name>Leto
|
||||
<!-- -->
|
||||
<!-- -->
|
||||
Atreides</h3>
|
||||
<script id=adjascent_test_nodes>
|
||||
const token = $('#token');
|
||||
testing.expectEqual('"puppeteer "', token.firstChild.nodeValue);
|
||||
|
||||
const name = $('#name');
|
||||
testing.expectEqual([
|
||||
"Leto\n ",
|
||||
" ",
|
||||
"\n ",
|
||||
" ",
|
||||
"\n Atreides"
|
||||
], Array.from(name.childNodes).map((n) => n.nodeValue));
|
||||
</script>
|
||||
|
||||
@@ -343,7 +343,7 @@ pub fn getOrCreateAttributeList(self: *Element, page: *Page) !*Attribute.List {
|
||||
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};
|
||||
a.* = .{ .normalize = self._namespace == .html };
|
||||
self._attributes = a;
|
||||
return a;
|
||||
}
|
||||
|
||||
@@ -118,6 +118,7 @@ pub const JsApi = struct {
|
||||
// in our Entry? Because that would require an extra 8 bytes for every single
|
||||
// attribute in the DOM, and, again, we expect that to almost always be null.
|
||||
pub const List = struct {
|
||||
normalize: bool,
|
||||
_list: std.DoublyLinkedList = .{},
|
||||
|
||||
pub fn isEmpty(self: *const List) bool {
|
||||
@@ -273,7 +274,9 @@ pub const List = struct {
|
||||
entry: ?*Entry,
|
||||
};
|
||||
fn getEntryAndNormalizedName(self: *const List, name: []const u8, page: *Page) !NormalizeAndEntry {
|
||||
const normalized = try normalizeNameForLookup(name, page);
|
||||
const normalized =
|
||||
if (self.normalize) try normalizeNameForLookup(name, page) else name;
|
||||
|
||||
return .{
|
||||
.normalized = normalized,
|
||||
.entry = self.getEntryWithNormalizedName(normalized),
|
||||
|
||||
Reference in New Issue
Block a user