mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-07-30 17:25:58 -04:00
webapi: Text.wholeText spans contiguous Text siblings
Fixes WPT /dom/nodes/Text-wholeText.html: wholeText must return the concatenated data of the contiguous exclusive Text nodes (adjacent Text siblings on both sides of the node) in tree order; we returned only the node's own data. The old own-data behavior is what every internal caller (semantic tree, markdown dump, AX names, textarea default value, textContent) actually wants, since they iterate the text nodes themselves; those now use the new Text.ownData helper, and getWholeText implements the spec walk (single-node case stays allocation-free). Coverage: /dom/nodes/Text-wholeText.html 0/1 -> 1/1 (fully green). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -139,7 +139,7 @@ fn walk(
|
||||
if (html_el.getHidden()) return;
|
||||
}
|
||||
} else if (node.is(CData.Text)) |text_node| {
|
||||
const text = text_node.getWholeText();
|
||||
const text = text_node.ownData();
|
||||
if (isAllWhitespace(text)) {
|
||||
return;
|
||||
}
|
||||
@@ -414,7 +414,7 @@ const JsonVisitor = struct {
|
||||
try self.jw.objectField("nodeType");
|
||||
try self.jw.write(3);
|
||||
try self.jw.objectField("nodeValue");
|
||||
try self.jw.write(text_node.getWholeText());
|
||||
try self.jw.write(text_node.ownData());
|
||||
} else {
|
||||
try self.jw.objectField("nodeType");
|
||||
try self.jw.write(9);
|
||||
@@ -472,7 +472,7 @@ const TextVisitor = struct {
|
||||
name_to_print = n;
|
||||
}
|
||||
} else if (node.is(CData.Text)) |text_node| {
|
||||
const trimmed = std.mem.trim(u8, text_node.getWholeText(), " \t\r\n");
|
||||
const trimmed = std.mem.trim(u8, text_node.ownData(), " \t\r\n");
|
||||
if (trimmed.len > 0) {
|
||||
name_to_print = trimmed;
|
||||
}
|
||||
|
||||
@@ -448,7 +448,7 @@ pub fn getTextContent(node: *Node, arena: Allocator) !?[]const u8 {
|
||||
}
|
||||
if (child.is(Node.CData)) |cdata| {
|
||||
if (cdata.is(Node.CData.Text)) |text| {
|
||||
const content = std.mem.trim(u8, text.getWholeText(), &std.ascii.whitespace);
|
||||
const content = std.mem.trim(u8, text.ownData(), &std.ascii.whitespace);
|
||||
if (content.len > 0) {
|
||||
if (single_chunk == null and arr.items.len == 0) {
|
||||
single_chunk = content;
|
||||
|
||||
@@ -142,7 +142,7 @@ fn isStandaloneAnchor(el: *Element) bool {
|
||||
|
||||
fn isSignificantText(node: *Node) bool {
|
||||
const text = node.is(Node.CData.Text) orelse return false;
|
||||
return !isAllWhitespace(text.getWholeText());
|
||||
return !isAllWhitespace(text.ownData());
|
||||
}
|
||||
|
||||
fn isVisibleElement(el: *Element) bool {
|
||||
|
||||
@@ -1175,11 +1175,11 @@ fn _normalize(self: *Node, allocator: Allocator, buffer: *std.ArrayList(u8), fra
|
||||
|
||||
if (next_node) |next| {
|
||||
if (next.is(CData.Text)) |_| {
|
||||
try buffer.appendSlice(allocator, text_node.getWholeText());
|
||||
try buffer.appendSlice(allocator, text_node.ownData());
|
||||
|
||||
while (next_node) |node_to_merge| {
|
||||
const next_text_node = node_to_merge.is(CData.Text) orelse break;
|
||||
try buffer.appendSlice(allocator, next_text_node.getWholeText());
|
||||
try buffer.appendSlice(allocator, next_text_node.ownData());
|
||||
|
||||
const to_remove = node_to_merge;
|
||||
next_node = node_to_merge.nextSibling();
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
// 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 js = @import("../../js/js.zig");
|
||||
const Frame = @import("../../Frame.zig");
|
||||
const Node = @import("../Node.zig");
|
||||
const CData = @import("../CData.zig");
|
||||
const Slot = @import("../element/html/Slot.zig");
|
||||
const slotting = @import("../element/slotting.zig");
|
||||
@@ -31,10 +34,41 @@ pub fn init(str: ?js.NullableString, frame: *Frame) !*Text {
|
||||
return node.as(Text);
|
||||
}
|
||||
|
||||
pub fn getWholeText(self: *Text) []const u8 {
|
||||
// This Text node's own data (getWholeText below spans adjacent Text nodes).
|
||||
pub fn ownData(self: *const Text) []const u8 {
|
||||
return self._proto._data.str();
|
||||
}
|
||||
|
||||
// The concatenated data of the contiguous exclusive Text nodes (adjacent
|
||||
// Text siblings on both sides of this one), in tree order.
|
||||
pub fn getWholeText(self: *Text, frame: *Frame) ![]const u8 {
|
||||
const node = self._proto.asNode();
|
||||
|
||||
var first = node;
|
||||
while (first.previousSibling()) |prev| {
|
||||
if (!isExclusiveTextNode(prev)) break;
|
||||
first = prev;
|
||||
}
|
||||
|
||||
// Common case: no adjacent text nodes, return our data directly.
|
||||
const has_next_text = if (node.nextSibling()) |next| isExclusiveTextNode(next) else false;
|
||||
if (first == node and !has_next_text) {
|
||||
return self._proto._data.str();
|
||||
}
|
||||
|
||||
var buf: std.ArrayList(u8) = .empty;
|
||||
var current: ?*Node = first;
|
||||
while (current) |cur| : (current = cur.nextSibling()) {
|
||||
if (!isExclusiveTextNode(cur)) break;
|
||||
try buf.appendSlice(frame.call_arena, cur._type.cdata._data.str());
|
||||
}
|
||||
return buf.items;
|
||||
}
|
||||
|
||||
fn isExclusiveTextNode(node: *const Node) bool {
|
||||
return node._type == .cdata and node._type.cdata._type == .text;
|
||||
}
|
||||
|
||||
pub fn getAssignedSlot(self: *Text, frame: *Frame) ?*Slot {
|
||||
return slotting.findSlot(self._proto.asNode(), true, frame);
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ pub fn getDefaultValue(self: *const TextArea) []const u8 {
|
||||
const node = self.asConstNode();
|
||||
if (node.firstChild()) |child| {
|
||||
if (child.is(Node.CData.Text)) |txt| {
|
||||
return txt.getWholeText();
|
||||
return txt.ownData();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
|
||||
@@ -989,7 +989,7 @@ fn writeName(
|
||||
},
|
||||
.cdata => |cd| switch (cd._type) {
|
||||
.text => |*text| {
|
||||
try writeString(text.getWholeText(), w);
|
||||
try writeString(text.ownData(), w);
|
||||
return .contents;
|
||||
},
|
||||
else => null,
|
||||
@@ -1109,7 +1109,7 @@ fn writeAccessibleNameFallback(node: *DOMNode, writer: *std.Io.Writer, frame: *F
|
||||
switch (child._type) {
|
||||
.cdata => |cd| switch (cd._type) {
|
||||
.text => |*text| {
|
||||
const content = std.mem.trim(u8, text.getWholeText(), &std.ascii.whitespace);
|
||||
const content = std.mem.trim(u8, text.ownData(), &std.ascii.whitespace);
|
||||
if (content.len > 0) {
|
||||
try writer.writeAll(content);
|
||||
try writer.writeByte(' ');
|
||||
|
||||
Reference in New Issue
Block a user