mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-08-02 18:59:36 -04:00
webapi: innerText uses StyleManager's visibility
innerText now uses the StyleManager's visibility check (isHidden) to skip rendering children which are hidden. Should allow us to close https://github.com/lightpanda-io/browser/pull/2741 and https://github.com/lightpanda-io/browser/issues/2734 Note that ~1000 WPT test still fail because of other style-driven behavior, most significantly is `white-space`. The isHidden change only fixes a couple WPT cases, but I think, for end users, it's the more important change (and it's also the simpler for us to implement). Having wrong spacing is usually less of a problem than showing the wrong content.
This commit is contained in:
@@ -11,6 +11,10 @@
|
||||
<p id=d4><span>Hello</span>
|
||||
<span>World</span></p>
|
||||
|
||||
<style>.ihide { display: none }</style>
|
||||
<div id=d5>a<span class=ihide>X</span>b<span style="display:none">Y</span>c</div>
|
||||
<div id=d6>before<div class=ihide>deep <span>nested</span></div>after</div>
|
||||
|
||||
<script id=innerHTML>
|
||||
const d1 = $('#d1');
|
||||
testing.expectEqual('hello <em>world</em>', d1.innerHTML);
|
||||
@@ -175,4 +179,10 @@
|
||||
testing.expectEqual("This is a\ntext", d2.innerText);
|
||||
testing.expectEqual("Hello World", $('#d3').innerText);
|
||||
testing.expectEqual("Hello World", $('#d4').innerText);
|
||||
|
||||
// display:none subtrees are excluded from innerText, whether the rule comes
|
||||
// from an inline style or a stylesheet (the common real-world case: content
|
||||
// hidden via a CSS class). A hidden block does not contribute a line break.
|
||||
testing.expectEqual("abc", $('#d5').innerText);
|
||||
testing.expectEqual("beforeafter", $('#d6').innerText);
|
||||
</script>
|
||||
|
||||
@@ -453,9 +453,9 @@ pub fn getLocalName(self: *Element) []const u8 {
|
||||
}
|
||||
|
||||
// Wrapper methods that delegate to Html implementations
|
||||
pub fn getInnerText(self: *Element, writer: *std.Io.Writer) !void {
|
||||
pub fn getInnerText(self: *Element, writer: *std.Io.Writer, frame: *Frame) !void {
|
||||
const he = self.is(Html) orelse return error.NotHtmlElement;
|
||||
return he.getInnerText(writer);
|
||||
return he.getInnerText(writer, frame);
|
||||
}
|
||||
|
||||
pub fn setInnerText(self: *Element, text: []const u8, frame: *Frame) !void {
|
||||
@@ -1913,9 +1913,9 @@ pub const JsApi = struct {
|
||||
pub const namespaceURI = bridge.accessor(Element.getNamespaceURI, null, .{});
|
||||
|
||||
pub const innerText = bridge.accessor(_innerText, Element.setInnerText, .{ .ce_reactions = true });
|
||||
fn _innerText(self: *Element, frame: *const Frame) ![]const u8 {
|
||||
fn _innerText(self: *Element, frame: *Frame) ![]const u8 {
|
||||
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
|
||||
try self.getInnerText(&buf.writer);
|
||||
try self.getInnerText(&buf.writer, frame);
|
||||
return buf.written();
|
||||
}
|
||||
|
||||
|
||||
@@ -216,13 +216,14 @@ pub fn asEventTarget(self: *HtmlElement) *@import("../EventTarget.zig") {
|
||||
return self._proto._proto._proto;
|
||||
}
|
||||
|
||||
pub fn getInnerText(self: *HtmlElement, writer: *std.Io.Writer) !void {
|
||||
pub fn getInnerText(self: *HtmlElement, writer: *std.Io.Writer, frame: *Frame) !void {
|
||||
const tag = self.asElement().getTag();
|
||||
switch (innerTextDisplay(self, tag)) {
|
||||
.skip, .replaced => return,
|
||||
else => {},
|
||||
}
|
||||
var state = InnerTextState{ .writer = writer, .preserve = tag == .pre };
|
||||
|
||||
var state = InnerTextState{ .writer = writer, .frame = frame, .preserve = tag == .pre };
|
||||
try self.collectInnerText(&state);
|
||||
}
|
||||
|
||||
@@ -1326,6 +1327,13 @@ pub fn reflectEnumerated(
|
||||
|
||||
const InnerTextState = struct {
|
||||
writer: *std.Io.Writer,
|
||||
|
||||
// Needed to reach the StyleManager for CSS-driven visibility (display:none).
|
||||
frame: *Frame,
|
||||
|
||||
// Tree-local visibility cache shared across the whole walk (see getInnerText).
|
||||
cache: Element.VisibilityCache = .{},
|
||||
|
||||
// number of line breaks we've accumulated for the block. Emitted lazily that
|
||||
// leading/trailing breaks aren't written and so that we can emit the max
|
||||
// requested, which can change as we render children.
|
||||
@@ -1450,6 +1458,10 @@ fn handleChildElement(
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.frame._style_manager.isHidden(he.asElement(), &state.cache, .{})) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (innerTextDisplay(he, tag)) {
|
||||
// Not rendered: skip the subtree entirely (script/style/metadata/...).
|
||||
.skip => {},
|
||||
@@ -1632,9 +1644,9 @@ pub const JsApi = struct {
|
||||
pub const constructor = bridge.constructor(HtmlElement.construct, .{ .new_target = true });
|
||||
|
||||
pub const innerText = bridge.accessor(_innerText, _setInnerText, .{ .ce_reactions = true });
|
||||
fn _innerText(self: *HtmlElement, frame: *const Frame) ![]const u8 {
|
||||
fn _innerText(self: *HtmlElement, frame: *Frame) ![]const u8 {
|
||||
var buf = std.Io.Writer.Allocating.init(frame.call_arena);
|
||||
try self.getInnerText(&buf.writer);
|
||||
try self.getInnerText(&buf.writer, frame);
|
||||
return buf.written();
|
||||
}
|
||||
fn _setInnerText(self: *HtmlElement, value: js.Value, frame: *Frame) !void {
|
||||
|
||||
@@ -1011,7 +1011,7 @@ fn writeName(
|
||||
|
||||
if (doc.getElementById(trimmed_id, frame)) |referenced_el| {
|
||||
// Get the text content of the referenced element
|
||||
try referenced_el.getInnerText(&buf.writer);
|
||||
try referenced_el.getInnerText(&buf.writer, frame);
|
||||
try buf.writer.writeByte(' ');
|
||||
has_content = true;
|
||||
}
|
||||
@@ -1262,7 +1262,7 @@ fn writeLabelInnerText(
|
||||
w: anytype,
|
||||
) !bool {
|
||||
var buf: std.Io.Writer.Allocating = .init(scratchAllocator(temp_arena, frame));
|
||||
try label_el.getInnerText(&buf.writer);
|
||||
try label_el.getInnerText(&buf.writer, frame);
|
||||
const text = std.mem.trim(u8, buf.written(), &std.ascii.whitespace);
|
||||
if (text.len == 0) return false;
|
||||
try writeString(text, w);
|
||||
|
||||
Reference in New Issue
Block a user