You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by sl...@apache.org on 2026/09/03 16:13:58 UTC
(maven-doxia) branch master updated: Render unknown Sink events as raw HTML in MarkdownSink
This is an automated email from the ASF dual-hosted git repository.
slachiewicz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-doxia.git
The following commit(s) were added to refs/heads/master by this push:
new 571fab11 Render unknown Sink events as raw HTML in MarkdownSink
571fab11 is described below
commit 571fab113ab51ee85f747642bba6384ddf9a40d9
Author: Sylwester Lachiewicz <sl...@apache.org>
AuthorDate: Mon Aug 24 12:55:30 2026 +0200
Render unknown Sink events as raw HTML in MarkdownSink
MarkdownSink silently dropped any Sink event it had no dedicated
handling for (e.g. <object>, image maps), taking the content that
event carried with it. Emit them as raw HTML instead, mirroring
Xhtml5BaseSink, but never self-close a non-void tag: HTML5 ignores
a trailing slash on those, which would otherwise swallow all
following content (see xdoc-selfclosing-raw-html for the matching
parser-side fix).
---
.../maven/doxia/module/markdown/MarkdownSink.java | 39 ++++++++++++++++++-
.../doxia/module/markdown/MarkdownSinkTest.java | 45 ++++++++++++++++++++++
2 files changed, 82 insertions(+), 2 deletions(-)
diff --git a/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java b/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java
index 690be2e5..ea534e74 100644
--- a/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java
+++ b/doxia-modules/doxia-module-markdown/src/main/java/org/apache/maven/doxia/module/markdown/MarkdownSink.java
@@ -38,6 +38,7 @@ import org.apache.maven.doxia.sink.impl.Xhtml5BaseSink;
import org.apache.maven.doxia.util.DoxiaStringUtils;
import org.apache.maven.doxia.util.DoxiaUtils;
import org.apache.maven.doxia.util.HtmlTools;
+import org.jsoup.parser.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -1360,15 +1361,49 @@ public class MarkdownSink extends Xhtml5BaseSink implements MarkdownMarkup {
write(text);
}
+ /** Table row-grouping tags without their own content: {@link #unknown(String, Object[], SinkEventAttributes)}
+ * ignores these rather than rendering them as raw HTML. */
+ private static final Collection<String> IGNORED_TABLE_GROUPING_TAGS =
+ Collections.unmodifiableList(Arrays.asList("thead", "tbody", "tfoot"));
+
/**
* {@inheritDoc}
*
- * Unknown events just log a warning message but are ignored otherwise.
+ * Delegates tag reconstruction and attribute escaping to
+ * {@link Xhtml5BaseSink#unknown(String, Object[], SinkEventAttributes)}, so any tag recognized by
+ * {@link HtmlTools#getHtmlTag(String)} (e.g. {@code <object>}, {@code <map>}/{@code <area>}) is emitted as raw
+ * HTML instead of being silently dropped.
+ * <p>
+ * Unlike that XHTML sink, a {@link #TAG_TYPE_SIMPLE} event for a non-void element is never self-closed
+ * ({@code <name/>}): HTML5 has no self-closing syntax outside the fixed list of void elements, so a
+ * self-closed non-void tag such as {@code <object/>} would otherwise be re-parsed as an open tag that
+ * swallows all following content as its own (hidden) fallback children. Such events are instead written as
+ * an explicitly closed pair ({@code <name></name>}).
+ * <p>
+ * {@code <thead>}/{@code <tbody>}/{@code <tfoot>} are excluded: they carry no content of their own (Doxia's
+ * table sink API already fully captures the table structure via {@code table()}/{@code tableRows()}/
+ * {@code tableRow()}/{@code tableCell()}), and {@link MarkdownParser} fires these as unknown events for
+ * every table it parses (they have no dedicated sink method). Rendering them as raw HTML would inject a
+ * tag directly before the first cell of a row, which breaks GFM pipe-table syntax since a table row must
+ * start at the beginning of its line.
+ *
* @see org.apache.maven.doxia.sink.Sink#unknown(String,Object[],SinkEventAttributes)
*/
@Override
public void unknown(String name, Object[] requiredParams, SinkEventAttributes attributes) {
- LOGGER.warn("{}Unknown Sink event '" + name + "', ignoring!", getLocationLogPrefix());
+ if (IGNORED_TABLE_GROUPING_TAGS.contains(name)) {
+ return;
+ }
+ if (requiredParams != null
+ && requiredParams[0] instanceof Integer
+ && (Integer) requiredParams[0] == TAG_TYPE_SIMPLE
+ && HtmlTools.getHtmlTag(name) != null
+ && !Tag.valueOf(name).isSelfClosing()) {
+ super.unknown(name, new Object[] {TAG_TYPE_START}, attributes);
+ super.unknown(name, new Object[] {TAG_TYPE_END}, null);
+ return;
+ }
+ super.unknown(name, requiredParams, attributes);
}
@Override
diff --git a/doxia-modules/doxia-module-markdown/src/test/java/org/apache/maven/doxia/module/markdown/MarkdownSinkTest.java b/doxia-modules/doxia-module-markdown/src/test/java/org/apache/maven/doxia/module/markdown/MarkdownSinkTest.java
index 713adaa7..5ca6c819 100644
--- a/doxia-modules/doxia-module-markdown/src/test/java/org/apache/maven/doxia/module/markdown/MarkdownSinkTest.java
+++ b/doxia-modules/doxia-module-markdown/src/test/java/org/apache/maven/doxia/module/markdown/MarkdownSinkTest.java
@@ -34,6 +34,7 @@ import org.apache.maven.doxia.sink.SinkEventAttributeSet;
import org.apache.maven.doxia.sink.SinkEventAttributeSet.Semantics;
import org.apache.maven.doxia.sink.impl.AbstractSinkTest;
import org.apache.maven.doxia.sink.impl.SinkEventTestingSink;
+import org.apache.maven.doxia.sink.impl.Xhtml5BaseSink;
import org.apache.maven.doxia.util.DoxiaStringUtils;
import org.junit.jupiter.api.Test;
@@ -741,4 +742,48 @@ class MarkdownSinkTest extends AbstractSinkTest {
+ "|<pre><code>code with | and ` inside</code></pre>|" + EOL + EOL;
assertEquals(expected, getSinkContent());
}
+
+ /**
+ * A self-closing {@code <object/>} (as xdoc's parser emits it, i.e. a single {@code unknown} event with
+ * {@link Xhtml5BaseSink#TAG_TYPE_SIMPLE}) is not a void HTML element, so it must never be rendered
+ * self-closed: HTML5 ignores the trailing slash on a non-void tag and would otherwise treat everything that
+ * follows as {@code <object>} fallback content. See MDOXIA-159.
+ */
+ @Test
+ void unknownObjectEventIsRenderedAsExplicitlyClosedRawHtml() throws IOException {
+ SinkEventAttributeSet attributes = new SinkEventAttributeSet();
+ attributes.addAttribute("type", "image/svg+xml");
+ attributes.addAttribute("data", "x.svg");
+
+ try (Sink sink = getSink()) {
+ sink.unknown("object", new Object[] {Xhtml5BaseSink.TAG_TYPE_SIMPLE}, attributes);
+ }
+
+ assertEquals("<object type=\"image/svg+xml\" data=\"x.svg\"></object>", getSinkContent());
+ }
+
+ /**
+ * An image map ({@code <map>} wrapping a void {@code <area/>}) must survive conversion instead of being
+ * silently dropped like any other unknown event previously was. Unlike {@code <object>}, {@code <area>} is a
+ * void element, so it is still fine to render it self-closed. See MDOXIA-159.
+ */
+ @Test
+ void unknownMapAndAreaEventsAreRenderedAsRawHtml() throws IOException {
+ SinkEventAttributeSet mapAttributes = new SinkEventAttributeSet();
+ mapAttributes.addAttribute("name", "imgmap");
+ SinkEventAttributeSet areaAttributes = new SinkEventAttributeSet();
+ areaAttributes.addAttribute("shape", "rect");
+ areaAttributes.addAttribute("coords", "0,0,10,10");
+ areaAttributes.addAttribute("href", "a.html");
+
+ try (Sink sink = getSink()) {
+ sink.unknown("map", new Object[] {Xhtml5BaseSink.TAG_TYPE_START}, mapAttributes);
+ sink.unknown("area", new Object[] {Xhtml5BaseSink.TAG_TYPE_SIMPLE}, areaAttributes);
+ sink.unknown("map", new Object[] {Xhtml5BaseSink.TAG_TYPE_END}, null);
+ }
+
+ assertEquals(
+ "<map name=\"imgmap\"><area shape=\"rect\" coords=\"0,0,10,10\" href=\"a.html\" /></map>",
+ getSinkContent());
+ }
}