summaryrefslogtreecommitdiffstats
path: root/src/htmlbuilder.h
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-02 17:46:21 +0200
committerDanilo M. <danix@danix.xyz>2026-08-02 17:46:21 +0200
commit2a10cebcfb978ce7c5f03a97473eafe9bb3d15dd (patch)
treefaec2dc7b5d9be72fac5e091b81eed3491d3c2d6 /src/htmlbuilder.h
parent8ecd5a8e83380f543b856ff28226a8e15fd7dc96 (diff)
downloadqtmaildir-2a10cebcfb978ce7c5f03a97473eafe9bb3d15dd.tar.gz
qtmaildir-2a10cebcfb978ce7c5f03a97473eafe9bb3d15dd.zip
feat: add HTML builder and cid: scheme handler
HtmlBuilder renders parsed messages (and whole threads, as one document, so newsletter threads don't spawn one Chromium process per message) into the HTML string the web view loads. Plain text is escaped and quote lines marked; the cid: rewrite is namespaced per message ("<prefix>!<id>") so two thread messages sharing a Content-ID don't collide. Hardened namespaceCids beyond the initial sketch after attacking it: handles unquoted cid: attribute values, background=/poster= (not just src/href), and CSS url(cid:...) in both style="" attributes and <style> blocks, all case-insensitively. Replaced the greedy [^"']+ capture with per-quote-style alternation so two cid: refs on one line can't bleed into each other. CidSchemeHandler serves cid: requests from the thread's inline-parts map, keyed by the same namespaced string, replaced wholesale per thread.
Diffstat (limited to 'src/htmlbuilder.h')
-rw-r--r--src/htmlbuilder.h50
1 files changed, 50 insertions, 0 deletions
diff --git a/src/htmlbuilder.h b/src/htmlbuilder.h
new file mode 100644
index 0000000..ac24e28
--- /dev/null
+++ b/src/htmlbuilder.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <QList>
+
+#include "mimeparser.h"
+
+/// One message's place in a rendered thread.
+struct ThreadRenderItem
+{
+ ParsedMessage message;
+
+ /// Matched messages render in full; unmatched collapse to a one-line stub.
+ bool expanded = true;
+
+ /// Disambiguates cid: references. Two newsletters in one thread commonly
+ /// use the same Content-ID (cid:logo@example.org), which would collide in
+ /// a single document, so every reference is rewritten to
+ /// cid:<prefix>!<id>.
+ QString cidPrefix;
+};
+
+/// Turns parsed messages into the HTML string handed to the web view.
+///
+/// Plain text goes through the same path as HTML so the view has one render
+/// path rather than two. A whole thread renders as ONE document rather than one
+/// view per message: a thread of newsletters can hold dozens of messages, and a
+/// QWebEngineView each would spawn a Chromium render process each.
+class HtmlBuilder
+{
+public:
+ enum Mode {
+ PreferHtml, ///< Use the HTML part when the message has one.
+ ForcePlain, ///< Always render the plain part, escaped.
+ };
+
+ /// Single message, used for the error card and for tests.
+ static QString build(const ParsedMessage &message, Mode mode);
+
+ /// The whole thread, oldest first.
+ static QString buildThread(const QList<ThreadRenderItem> &items, Mode mode);
+
+ /// Rewrites cid: URLs in an HTML body to their namespaced form.
+ static QString namespaceCids(const QString &html, const QString &prefix);
+
+private:
+ static QString renderPlain(const QString &text);
+ static QString renderBody(const ThreadRenderItem &item, Mode mode);
+ static QString renderStub(const ParsedMessage &message);
+ static QString document(const QString &bodyHtml);
+};