mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-10-08 20:32:00 -04:00
Merge pull request #3663 from lightpanda-io/markdown-anchor-no-href
Treat <a> without href as text in markdown and clutter strip
This commit is contained in:
3 files changed
+48
-18
No files matched your search
@@ -343,7 +343,7 @@ fn measureChild(self: *const RenderTree, child: Child, where: Measure.Where, m:
|
||||
const el = child.node.subtype(Element);
|
||||
const inner: Measure.Where = .{
|
||||
.shell = where.shell or dump_html.isShellElement(el),
|
||||
.link = where.link or el.getTag() == .anchor,
|
||||
.link = where.link or (el.getTag() == .anchor and el.getAttributeInterned("href") != null),
|
||||
};
|
||||
if (el.is(Slot)) |slot| {
|
||||
var it = self.slotted(slot);
|
||||
|
||||
+12
-2
@@ -286,9 +286,10 @@ const Pass = struct {
|
||||
|
||||
var inner = ctx;
|
||||
switch (tag) {
|
||||
.anchor => {
|
||||
// Unlike readability: without href, <a> is a placeholder
|
||||
// (tag chips, JS buttons), and its text is not link text.
|
||||
.anchor => if (el.getAttributeSafe(comptime .wrap("href"))) |href| {
|
||||
inner.in_link = true;
|
||||
const href = el.getAttributeSafe(comptime .wrap("href")) orelse "";
|
||||
inner.hash_link = href.len > 0 and href[0] == '#';
|
||||
},
|
||||
.h1, .h2, .h3, .h4, .h5, .h6 => inner.in_heading = true,
|
||||
@@ -921,6 +922,15 @@ test "clutter: qualifying sibling paragraphs come along" {
|
||||
try testing.expectEqual(null, std.mem.indexOf(u8, out, "[a]"));
|
||||
}
|
||||
|
||||
test "clutter: anchors without href are not link text" {
|
||||
const quote = "<div class=\"quote\"><span>" ++ prose ++ "</span><div class=\"tags\">Tags: <a class=\"tag\">bread</a></div></div>";
|
||||
// A short quote with many tags: over 0.2 link density if tags counted.
|
||||
const tagged = "<div class=\"quote\"><span>I have not failed. I've just found ten thousand ways that won't work.</span><span>by Thomas A. Edison</span>" ++
|
||||
"<div class=\"tags\">Tags: <a class=\"tag\">edison</a> <a class=\"tag\">failure</a> <a class=\"tag\">inspirational</a> <a class=\"tag\">paraphrased</a></div></div>";
|
||||
const out = try extract("<div class=\"quotes\">" ++ quote ++ quote ++ tagged ++ quote ++ "</div>");
|
||||
try testing.expectEqual(true, std.mem.indexOf(u8, out, "ten thousand ways") != null);
|
||||
}
|
||||
|
||||
test "clutter: the prune set lives in the caller's allocator" {
|
||||
const frame = try testing.createFrame();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
+35
-15
@@ -82,9 +82,11 @@ const Context = struct {
|
||||
// what follows after the children
|
||||
const Epilogue = union(enum) {
|
||||
none,
|
||||
// A standalone anchor without href: its own line, but no link syntax.
|
||||
standalone_text,
|
||||
element: Element.Tag,
|
||||
block_anchor: struct { href: ?[]const u8, label: ?[]const u8 },
|
||||
inline_anchor: struct { href: ?[]const u8, standalone: bool },
|
||||
block_anchor: struct { href: []const u8, label: ?[]const u8 },
|
||||
inline_anchor: struct { href: []const u8, standalone: bool },
|
||||
};
|
||||
|
||||
fn deinit(self: *Context) void {
|
||||
@@ -259,15 +261,18 @@ const Context = struct {
|
||||
},
|
||||
.anchor => {
|
||||
const frame = self.frame;
|
||||
// Without href, <a> is a placeholder, not a hyperlink.
|
||||
const href_raw = el.getAttributeInterned("href") orelse {
|
||||
if (!RenderTree.isStandaloneAnchor(el, frame)) {
|
||||
return self.open(.init(self.tree.content(el, boxed)), .none);
|
||||
}
|
||||
try self.ensureNewline();
|
||||
return self.open(.init(self.tree.content(el, boxed)), .standalone_text);
|
||||
};
|
||||
|
||||
const info = RenderTree.analyzeContent(el.asNode(), frame);
|
||||
const label = getAnchorLabel(el);
|
||||
const href_raw = el.getAttributeInterned("href");
|
||||
|
||||
if (!info.has_visible and label == null and href_raw == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const href = if (href_raw) |h| URL.resolve(frame.local_arena, frame.base(), h, .{ .encoding = frame.charset }) catch h else null;
|
||||
const href = URL.resolve(frame.local_arena, frame.base(), href_raw, .{ .encoding = frame.charset }) catch href_raw;
|
||||
|
||||
if (info.has_block) {
|
||||
return self.open(.init(self.tree.content(el, boxed)), .{ .block_anchor = .{
|
||||
@@ -309,22 +314,23 @@ const Context = struct {
|
||||
fn close(self: *Context, epilogue: Epilogue) Error!void {
|
||||
const tag = switch (epilogue) {
|
||||
.none => return,
|
||||
.standalone_text => {
|
||||
try self.ensureNewline();
|
||||
return;
|
||||
},
|
||||
.element => |t| t,
|
||||
.block_anchor => |anchor| {
|
||||
const href = anchor.href orelse return;
|
||||
try self.ensureNewline();
|
||||
try self.writer.writeByte('[');
|
||||
try self.writer.writeAll(anchor.label orelse href);
|
||||
try self.writer.writeAll(anchor.label orelse anchor.href);
|
||||
try self.writer.writeAll("](");
|
||||
try self.writer.writeAll(href);
|
||||
try self.writer.writeAll(anchor.href);
|
||||
try self.writer.writeAll(")\n");
|
||||
return;
|
||||
},
|
||||
.inline_anchor => |anchor| {
|
||||
try self.writer.writeAll("](");
|
||||
if (anchor.href) |h| {
|
||||
try self.writer.writeAll(h);
|
||||
}
|
||||
try self.writer.writeAll(anchor.href);
|
||||
try self.writer.writeByte(')');
|
||||
if (anchor.standalone) {
|
||||
try self.writer.writeByte('\n');
|
||||
@@ -756,6 +762,20 @@ test "browser.markdown: anchor fallback label" {
|
||||
, "[](http://localhost/no-label)\n");
|
||||
}
|
||||
|
||||
test "browser.markdown: anchor without href is plain text" {
|
||||
try testMarkdownHTML(
|
||||
\\<p>Read the <a class="term">glossary</a> first.</p>
|
||||
, "\nRead the glossary first.\n");
|
||||
|
||||
try testMarkdownHTML(
|
||||
\\<nav><a class="x">Home</a><a class="x"><b>About</b></a></nav>
|
||||
, "Home\n**About**\n");
|
||||
|
||||
try testMarkdownHTML(
|
||||
\\<a aria-label="Menu"><svg></svg></a>
|
||||
, "");
|
||||
}
|
||||
|
||||
test "browser.markdown: hidden elements are skipped" {
|
||||
try testMarkdownHTML(
|
||||
\\<p>before</p>
|
||||
|
||||
Reference in new issue
Block a user