MailJDTourDownloadDocsQualityTestsCoverage

Which source lines and branches the tests execute. Lines 91.5 %, branches 75.0 % (minimum 85 % and 72 %). main @ 399ffe3d · 21 October 2026 · pipeline 7238.

GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 96.3% 419 / 0 / 435
Functions: 94.7% 18 / 0 / 19
Branches: 84.4% 853 / 0 / 1011

service/NativeMailDocument.cpp
Line Branch Exec Source
1 #include "NativeMailDocument.h"
2 #include "util/CanonicalContentId.h"
3 #include <QSet>
4 #include <QMap>
5 #include <QStringList>
6 #include <QUrl>
7 #include <QVector>
8 #include <array>
9 #include <functional>
10 #include <QColor>
11 #include <QHash>
12 #include <QRegularExpression>
13 #include <libxml/HTMLparser.h>
14
15 namespace {
16 struct Node {
17 QString tag, text, href, direction, contentId, style;
18 QUrl remoteUrl;
19 QByteArray imageData; // Compose: a decoded data: image
20 QString imageType;
21 int imageWidth = 0, imageHeight = 0;
22 int region = 0; // Compose: 1 signature, 2 quote (MailJD's markers)
23 int columns = 1, rows = 1;
24 QVector<int> children;
25 };
26 struct Frame {
27 int node = 0;
28 bool suppressed = false;
29 };
30 struct State {
31 htmlParserCtxtPtr parser = nullptr;
32 QVector<Node> nodes{Node{}};
33 QVector<Frame> stack{Frame{}};
34 bool limited = false, simplified = false, wasCancelled = false;
35 bool compose = false, wordHtml = false;
36 const std::atomic_bool *cancelled = nullptr;
37 115894 bool interrupted() {
38
6/6
✓ Branch 2 → 3 taken 59457 times.
✓ Branch 2 → 6 taken 56437 times.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 6 taken 59455 times.
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 10 taken 115892 times.
115894 if (cancelled && cancelled->load(std::memory_order_relaxed)) {
39 2 wasCancelled = true;
40
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 2 times.
2 if (parser)
41 ✗ xmlStopParser(parser);
42 }
43
4/4
✓ Branch 10 → 11 taken 115890 times.
✓ Branch 10 → 12 taken 4 times.
✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 13 taken 115888 times.
115894 return limited || wasCancelled;
44 }
45 int deniedEntities = 0;
46 qsizetype textCharacters = 0;
47 3 void stop() {
48 3 limited = true;
49
1/2
✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 4 not taken.
3 if (parser)
50 3 xmlStopParser(parser);
51 3 }
52 32596 void appendText(const QString &text) {
53
8/10
✓ Branch 2 → 3 taken 32596 times.
✓ Branch 3 → 4 taken 32596 times.
✗ Branch 3 → 8 not taken.
✓ Branch 4 → 5 taken 32596 times.
✓ Branch 5 → 6 taken 32545 times.
✓ Branch 5 → 8 taken 51 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 32545 times.
✓ Branch 10 → 11 taken 51 times.
✓ Branch 10 → 12 taken 32545 times.
32596 if (interrupted() || stack.last().suppressed || text.isEmpty())
54 457 return;
55 32545 textCharacters += text.size();
56
1/2
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 16 taken 32545 times.
32545 if (textCharacters > 1024 * 1024) {
57 ✗ stop();
58 ✗ return;
59 }
60
1/1
✓ Branch 16 → 17 taken 32545 times.
32545 const int parent = stack.last().node;
61
3/3
✓ Branch 17 → 18 taken 32545 times.
✓ Branch 19 → 20 taken 10483 times.
✓ Branch 19 → 28 taken 22062 times.
32545 if (!nodes[parent].children.isEmpty()) {
62
2/2
✓ Branch 20 → 21 taken 10483 times.
✓ Branch 21 → 22 taken 10483 times.
10483 const int last = nodes[parent].children.last();
63
3/3
✓ Branch 22 → 23 taken 10483 times.
✓ Branch 24 → 25 taken 405 times.
✓ Branch 24 → 28 taken 10078 times.
10483 if (nodes[last].tag.isEmpty()) {
64
2/2
✓ Branch 25 → 26 taken 405 times.
✓ Branch 26 → 27 taken 405 times.
405 nodes[last].text += text;
65 405 return;
66 }
67 }
68
2/2
✓ Branch 29 → 30 taken 1 time.
✓ Branch 29 → 32 taken 32139 times.
32140 if (nodes.size() >= NativeMailDocument::MaxNodes) {
69
1/1
✓ Branch 30 → 31 taken 1 time.
1 stop();
70 1 return;
71 }
72 32139 const int next = nodes.size();
73
2/2
✓ Branch 33 → 34 taken 32139 times.
✓ Branch 34 → 35 taken 32139 times.
32139 nodes[parent].children.append(next);
74
1/1
✓ Branch 35 → 36 taken 32139 times.
32139 Node node;
75 32139 node.text = text;
76
1/1
✓ Branch 37 → 38 taken 32139 times.
32139 nodes.append(node);
77 32139 }
78 };
79 84276 QString attribute(const xmlChar **attributes, const char *name) {
80
2/2
✓ Branch 2 → 3 taken 60431 times.
✓ Branch 2 → 4 taken 23845 times.
84276 if (!attributes)
81 60431 return {};
82
3/4
✓ Branch 12 → 13 taken 24503 times.
✓ Branch 12 → 14 taken 17258 times.
✓ Branch 13 → 5 taken 24503 times.
✗ Branch 13 → 14 not taken.
41761 for (int i = 0; attributes[i] && attributes[i + 1]; i += 2)
83
2/2
✓ Branch 6 → 7 taken 6587 times.
✓ Branch 6 → 11 taken 17916 times.
24503 if (qstrcmp(reinterpret_cast<const char *>(attributes[i]), name) == 0)
84
1/1
✓ Branch 8 → 9 taken 6587 times.
6587 return QString::fromUtf8(reinterpret_cast<const char *>(attributes[i + 1]));
85 17258 return {};
86 }
87 // Sprint 108 (D-9) composer styles: canonical values only — no functions
88 // other than rgb()/rgba(), no URLs, no escapes.
89 56 QString composeColor(const QString &value) {
90
4/7
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 15 taken 53 times.
✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 15 not taken.
✓ Branch 9 → 10 taken 3 times.
✗ Branch 121 → 122 not taken.
✗ Branch 121 → 123 not taken.
59 static const QRegularExpression hex(QStringLiteral("^#([0-9a-f]{3}|[0-9a-f]{6})$"));
91 6 static const QRegularExpression rgb(QStringLiteral(
92
4/7
✓ Branch 15 → 16 taken 3 times.
✓ Branch 15 → 28 taken 53 times.
✓ Branch 17 → 18 taken 3 times.
✗ Branch 17 → 28 not taken.
✓ Branch 22 → 23 taken 3 times.
✗ Branch 131 → 132 not taken.
✗ Branch 131 → 133 not taken.
62 "^rgba?\\(\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*(?:,\\s*([0-9.]{1,6})\\s*)?\\)$"));
93
4/7
✓ Branch 28 → 29 taken 3 times.
✓ Branch 28 → 41 taken 53 times.
✓ Branch 30 → 31 taken 3 times.
✗ Branch 30 → 41 not taken.
✓ Branch 35 → 36 taken 3 times.
✗ Branch 141 → 142 not taken.
✗ Branch 141 → 143 not taken.
59 static const QRegularExpression name(QStringLiteral("^[a-z]{3,20}$"));
94
5/5
✓ Branch 42 → 43 taken 56 times.
✓ Branch 43 → 44 taken 56 times.
✓ Branch 45 → 46 taken 43 times.
✓ Branch 45 → 50 taken 13 times.
✓ Branch 47 → 48 taken 43 times.
56 if (hex.match(value).hasMatch()) return QColor(value).name();
95
1/1
✓ Branch 51 → 52 taken 13 times.
13 const auto match = rgb.match(value);
96
3/3
✓ Branch 52 → 53 taken 13 times.
✓ Branch 53 → 54 taken 3 times.
✓ Branch 53 → 84 taken 10 times.
13 if (match.hasMatch()) {
97
5/17
✓ Branch 54 → 55 taken 3 times.
✗ Branch 56 → 57 not taken.
✓ Branch 56 → 61 taken 3 times.
✗ Branch 57 → 58 not taken.
✗ Branch 58 → 59 not taken.
✗ Branch 59 → 60 not taken.
✗ Branch 59 → 61 not taken.
✗ Branch 62 → 63 not taken.
✓ Branch 62 → 64 taken 3 times.
✓ Branch 64 → 65 taken 3 times.
✗ Branch 64 → 66 not taken.
✗ Branch 66 → 67 not taken.
✓ Branch 66 → 68 taken 3 times.
✗ Branch 150 → 151 not taken.
✗ Branch 150 → 152 not taken.
✗ Branch 154 → 155 not taken.
✗ Branch 154 → 156 not taken.
3 if (!match.captured(4).isEmpty() && match.captured(4).toDouble() < 0.1) return {};
98
2/2
✓ Branch 72 → 73 taken 3 times.
✓ Branch 75 → 76 taken 3 times.
9 return QColor(qMin(255, match.captured(1).toInt()), qMin(255, match.captured(2).toInt()),
99
5/5
✓ Branch 68 → 69 taken 3 times.
✓ Branch 69 → 70 taken 3 times.
✓ Branch 71 → 72 taken 3 times.
✓ Branch 74 → 75 taken 3 times.
✓ Branch 78 → 79 taken 3 times.
12 qMin(255, match.captured(3).toInt())).name();
100 }
101
4/7
✓ Branch 85 → 86 taken 10 times.
✓ Branch 90 → 91 taken 8 times.
✗ Branch 90 → 101 not taken.
✓ Branch 104 → 105 taken 7 times.
✓ Branch 104 → 110 taken 3 times.
✗ Branch 178 → 179 not taken.
✗ Branch 178 → 180 not taken.
28 if (name.match(value).hasMatch() && value != QLatin1String("transparent") &&
102
8/11
✓ Branch 86 → 87 taken 10 times.
✓ Branch 87 → 88 taken 8 times.
✓ Branch 87 → 101 taken 2 times.
✓ Branch 93 → 94 taken 8 times.
✗ Branch 93 → 101 not taken.
✓ Branch 96 → 97 taken 8 times.
✗ Branch 96 → 101 not taken.
✓ Branch 99 → 100 taken 7 times.
✓ Branch 99 → 101 taken 1 time.
✓ Branch 102 → 103 taken 10 times.
✗ Branch 102 → 104 not taken.
28 value != QLatin1String("inherit") && value != QLatin1String("initial") && QColor::isValidColorName(value))
103
1/1
✓ Branch 107 → 108 taken 7 times.
7 return QColor::fromString(value).name();
104 3 return {};
105 13 }
106 // A CSS length as whole pixels (px, pt, em at 16 px); -1 for anything else.
107 370 int composeLengthPixels(const QString &value) {
108
4/7
✓ Branch 2 → 3 taken 8 times.
✓ Branch 2 → 15 taken 362 times.
✓ Branch 4 → 5 taken 8 times.
✗ Branch 4 → 15 not taken.
✓ Branch 9 → 10 taken 8 times.
✗ Branch 51 → 52 not taken.
✗ Branch 51 → 53 not taken.
378 static const QRegularExpression length(QStringLiteral("^(-?[0-9]{1,4}(?:\\.[0-9]{1,3})?)(px|pt|em|rem)?$"));
109
1/1
✓ Branch 16 → 17 taken 370 times.
370 const auto match = length.match(value);
110
3/3
✓ Branch 17 → 18 taken 370 times.
✓ Branch 18 → 19 taken 21 times.
✓ Branch 18 → 20 taken 349 times.
370 if (!match.hasMatch()) return -1;
111
2/2
✓ Branch 20 → 21 taken 349 times.
✓ Branch 21 → 22 taken 349 times.
349 const double number = match.captured(1).toDouble();
112
1/1
✓ Branch 23 → 24 taken 349 times.
349 const QString unit = match.captured(2);
113
2/2
✓ Branch 26 → 27 taken 4 times.
✓ Branch 26 → 28 taken 345 times.
694 const double pixels = unit == QLatin1String("pt") ? number * 4.0 / 3.0
114
3/4
✓ Branch 30 → 31 taken 337 times.
✓ Branch 30 → 34 taken 8 times.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 337 times.
345 : (unit == QLatin1String("em") || unit == QLatin1String("rem")) ? number * 16.0
115 349 : number;
116 349 return qBound(0, qRound(pixels), 400);
117 370 }
118 34 QString composeFontSize(const QString &value) {
119 ✗ static const QHash<QString, int> keywords{{"xx-small", 8}, {"x-small", 8}, {"small", 10}, {"medium", 12},
120
5/10
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 19 taken 32 times.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 19 not taken.
✓ Branch 16 → 17 taken 14 times.
✓ Branch 16 → 18 taken 2 times.
✗ Branch 80 → 81 not taken.
✗ Branch 80 → 82 not taken.
✗ Branch 96 → 97 not taken.
✗ Branch 96 → 98 not taken.
50 {"large", 14}, {"x-large", 18}, {"xx-large", 24}};
121
4/4
✓ Branch 20 → 21 taken 2 times.
✓ Branch 20 → 32 taken 32 times.
✓ Branch 25 → 26 taken 2 times.
✓ Branch 26 → 27 taken 2 times.
36 if (keywords.contains(value)) return QString::number(keywords.value(value)) + QStringLiteral("pt");
122
4/7
✓ Branch 32 → 33 taken 2 times.
✓ Branch 32 → 45 taken 30 times.
✓ Branch 34 → 35 taken 2 times.
✗ Branch 34 → 45 not taken.
✓ Branch 39 → 40 taken 2 times.
✗ Branch 115 → 116 not taken.
✗ Branch 115 → 117 not taken.
34 static const QRegularExpression size(QStringLiteral("^([0-9]{1,3}(?:\\.[0-9]{1,2})?)(px|pt|em|rem)$"));
123
1/1
✓ Branch 46 → 47 taken 32 times.
32 const auto match = size.match(value);
124
2/3
✓ Branch 47 → 48 taken 32 times.
✗ Branch 48 → 49 not taken.
✓ Branch 48 → 50 taken 32 times.
32 if (!match.hasMatch()) return {};
125
2/2
✓ Branch 50 → 51 taken 32 times.
✓ Branch 51 → 52 taken 32 times.
32 const double number = match.captured(1).toDouble();
126
1/1
✓ Branch 53 → 54 taken 32 times.
32 const QString unit = match.captured(2);
127
2/2
✓ Branch 56 → 57 taken 4 times.
✓ Branch 56 → 58 taken 28 times.
60 const double points = unit == QLatin1String("px") ? number * 0.75
128
1/2
✓ Branch 60 → 61 taken 28 times.
✗ Branch 60 → 62 not taken.
60 : unit == QLatin1String("pt") ? number : number * 12.0;
129
2/2
✓ Branch 69 → 70 taken 32 times.
✓ Branch 70 → 71 taken 32 times.
32 return QString::number(qBound(8, qRound(points), 36)) + QStringLiteral("pt");
130 34 }
131 132 QString composeFontFamily(const QString &value) {
132 // Names only: letters, digits, spaces, commas, quotes and hyphens. The
133 // import maps the list onto the fixed D-6 set.
134
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 132 times.
132 if (value.size() > 160) return {};
135
4/7
✓ Branch 5 → 6 taken 7 times.
✓ Branch 5 → 18 taken 125 times.
✓ Branch 7 → 8 taken 7 times.
✗ Branch 7 → 18 not taken.
✓ Branch 12 → 13 taken 7 times.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
139 static const QRegularExpression families(QStringLiteral("^[a-z0-9 ,'\"-]+$"));
136
4/4
✓ Branch 19 → 20 taken 132 times.
✓ Branch 20 → 21 taken 132 times.
✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 24 taken 131 times.
132 if (!families.match(value).hasMatch()) return {};
137 131 QString out = value;
138
1/1
✓ Branch 29 → 30 taken 131 times.
131 out.replace(QLatin1Char('"'), QLatin1Char('\''));
139 131 return out;
140 131 }
141
142 28502 QString safeInlineStyle(const QString &source, bool &simplified, QString &direction, bool compose = false) {
143
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 28501 times.
28502 if (source.size() > 2048) { simplified = true; return {}; }
144 28501 QMap<QString, QString> output;
145 28501 int declarations = 0;
146
5/5
✓ Branch 7 → 8 taken 28501 times.
✓ Branch 8 → 9 taken 28501 times.
✓ Branch 9 → 10 taken 28501 times.
✓ Branch 335 → 11 taken 28824 times.
✓ Branch 335 → 336 taken 28500 times.
57324 for (const QString &declaration : source.split(';')) {
147
2/2
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 14 taken 28823 times.
28824 if (++declarations > 32) { simplified = true; break; }
148
3/3
✓ Branch 14 → 15 taken 28823 times.
✓ Branch 17 → 18 taken 27800 times.
✓ Branch 17 → 19 taken 1023 times.
29100 if (declaration.trimmed().isEmpty()) continue;
149 1023 const qsizetype colon = declaration.indexOf(':');
150
2/2
✓ Branch 21 → 22 taken 1 time.
✓ Branch 21 → 23 taken 1022 times.
1023 if (colon < 0) { simplified = true; continue; }
151
3/3
✓ Branch 23 → 24 taken 1022 times.
✓ Branch 24 → 25 taken 1022 times.
✓ Branch 25 → 26 taken 1022 times.
1022 const QString property = declaration.left(colon).trimmed().toLower();
152
3/3
✓ Branch 28 → 29 taken 1022 times.
✓ Branch 29 → 30 taken 1022 times.
✓ Branch 30 → 31 taken 1022 times.
1022 QString value = declaration.mid(colon + 1).trimmed().toLower();
153 1022 bool allowed = false;
154
2/2
✓ Branch 34 → 35 taken 28 times.
✓ Branch 34 → 68 taken 994 times.
1022 if (property == "font-weight") {
155
7/7
✓ Branch 36 → 37 taken 17 times.
✓ Branch 36 → 39 taken 11 times.
✓ Branch 38 → 39 taken 1 time.
✓ Branch 38 → 40 taken 16 times.
✓ Branch 41 → 42 taken 12 times.
✓ Branch 41 → 43 taken 16 times.
✓ Branch 42 → 61 taken 12 times.
28 if (value == "bold" || value == "bolder") value = "bold";
156
7/7
✓ Branch 44 → 45 taken 12 times.
✓ Branch 44 → 47 taken 4 times.
✓ Branch 46 → 47 taken 1 time.
✓ Branch 46 → 48 taken 11 times.
✓ Branch 49 → 50 taken 5 times.
✓ Branch 49 → 51 taken 11 times.
✓ Branch 50 → 61 taken 5 times.
16 else if (value == "normal" || value == "lighter") value = "normal";
157 else {
158 11 bool numeric = false;
159
1/1
✓ Branch 51 → 52 taken 11 times.
11 const int weight = value.toInt(&numeric);
160
8/8
✓ Branch 52 → 53 taken 10 times.
✓ Branch 52 → 60 taken 1 time.
✓ Branch 53 → 54 taken 9 times.
✓ Branch 53 → 60 taken 1 time.
✓ Branch 54 → 55 taken 8 times.
✓ Branch 54 → 60 taken 1 time.
✓ Branch 55 → 56 taken 7 times.
✓ Branch 55 → 60 taken 1 time.
11 if (numeric && weight >= 100 && weight <= 900 && weight % 100 == 0)
161
3/3
✓ Branch 56 → 57 taken 4 times.
✓ Branch 56 → 58 taken 3 times.
✓ Branch 59 → 60 taken 7 times.
7 value = weight >= 600 ? "bold" : "normal";
162 }
163
4/4
✓ Branch 62 → 63 taken 12 times.
✓ Branch 62 → 65 taken 16 times.
✓ Branch 64 → 65 taken 8 times.
✓ Branch 64 → 66 taken 4 times.
28 allowed = value == "bold" || value == "normal";
164
2/2
✓ Branch 69 → 70 taken 4 times.
✓ Branch 69 → 79 taken 990 times.
994 } else if (property == "font-style") {
165
6/6
✓ Branch 71 → 72 taken 3 times.
✓ Branch 71 → 76 taken 1 time.
✓ Branch 73 → 74 taken 2 times.
✓ Branch 73 → 76 taken 1 time.
✓ Branch 75 → 76 taken 1 time.
✓ Branch 75 → 77 taken 1 time.
4 allowed = value == "normal" || value == "italic" || value == "oblique";
166
2/2
✓ Branch 80 → 81 taken 8 times.
✓ Branch 80 → 115 taken 982 times.
990 } else if (property == "text-decoration") {
167
2/2
✓ Branch 81 → 82 taken 8 times.
✓ Branch 84 → 85 taken 8 times.
16 const auto tokens = value.simplified().split(' ');
168
3/4
✓ Branch 87 → 88 taken 8 times.
✗ Branch 87 → 91 not taken.
✓ Branch 89 → 90 taken 7 times.
✓ Branch 89 → 91 taken 1 time.
8 allowed = !tokens.isEmpty() && tokens.size() <= 3;
169
2/2
✓ Branch 108 → 94 taken 13 times.
✓ Branch 108 → 109 taken 8 times.
21 for (const auto &token : tokens)
170
10/10
✓ Branch 95 → 96 taken 9 times.
✓ Branch 95 → 105 taken 4 times.
✓ Branch 97 → 98 taken 7 times.
✓ Branch 97 → 104 taken 2 times.
✓ Branch 99 → 100 taken 6 times.
✓ Branch 99 → 104 taken 1 time.
✓ Branch 101 → 102 taken 5 times.
✓ Branch 101 → 104 taken 1 time.
✓ Branch 103 → 104 taken 3 times.
✓ Branch 103 → 105 taken 2 times.
13 allowed = allowed && (token == "underline" || token == "line-through" || token == "overline" || token == "none");
171
1/1
✓ Branch 110 → 111 taken 8 times.
8 value = tokens.join(' ');
172
2/2
✓ Branch 116 → 117 taken 18 times.
✓ Branch 116 → 128 taken 964 times.
990 } else if (property == "text-align") {
173
8/8
✓ Branch 118 → 119 taken 17 times.
✓ Branch 118 → 125 taken 1 time.
✓ Branch 120 → 121 taken 11 times.
✓ Branch 120 → 125 taken 6 times.
✓ Branch 122 → 123 taken 4 times.
✓ Branch 122 → 125 taken 7 times.
✓ Branch 124 → 125 taken 3 times.
✓ Branch 124 → 126 taken 1 time.
18 allowed = value == "left" || value == "right" || value == "center" || value == "justify";
174
2/2
✓ Branch 129 → 130 taken 2 times.
✓ Branch 129 → 138 taken 962 times.
964 } else if (property == "direction") {
175
3/4
✓ Branch 131 → 132 taken 1 time.
✓ Branch 131 → 134 taken 1 time.
✗ Branch 133 → 134 not taken.
✓ Branch 133 → 135 taken 1 time.
2 allowed = value == "rtl" || value == "ltr";
176
2/2
✓ Branch 136 → 137 taken 1 time.
✓ Branch 136 → 312 taken 1 time.
2 if (allowed) direction = value;
177
2/2
✓ Branch 139 → 140 taken 4 times.
✓ Branch 139 → 149 taken 958 times.
962 } else if (property == "white-space") {
178
6/6
✓ Branch 141 → 142 taken 3 times.
✓ Branch 141 → 146 taken 1 time.
✓ Branch 143 → 144 taken 2 times.
✓ Branch 143 → 146 taken 1 time.
✓ Branch 145 → 146 taken 1 time.
✓ Branch 145 → 147 taken 1 time.
4 allowed = value == "normal" || value == "pre" || value == "pre-wrap";
179
10/10
✓ Branch 149 → 150 taken 702 times.
✓ Branch 149 → 157 taken 256 times.
✓ Branch 151 → 152 taken 662 times.
✓ Branch 151 → 156 taken 40 times.
✓ Branch 153 → 154 taken 653 times.
✓ Branch 153 → 156 taken 9 times.
✓ Branch 155 → 156 taken 7 times.
✓ Branch 155 → 157 taken 646 times.
✓ Branch 158 → 159 taken 56 times.
✓ Branch 158 → 176 taken 902 times.
958 } else if (compose && (property == "color" || property == "background-color" || property == "background")) {
180
1/1
✓ Branch 159 → 160 taken 56 times.
56 value = composeColor(value);
181 56 allowed = !value.isEmpty();
182
6/6
✓ Branch 163 → 164 taken 53 times.
✓ Branch 163 → 167 taken 3 times.
✓ Branch 165 → 166 taken 6 times.
✓ Branch 165 → 167 taken 47 times.
✓ Branch 168 → 169 taken 6 times.
✓ Branch 168 → 312 taken 50 times.
56 if (allowed && property == "background") {
183
1/1
✓ Branch 172 → 173 taken 6 times.
6 output.insert(QStringLiteral("background-color"), value);
184 6 continue;
185 }
186
6/6
✓ Branch 176 → 177 taken 646 times.
✓ Branch 176 → 180 taken 256 times.
✓ Branch 178 → 179 taken 34 times.
✓ Branch 178 → 180 taken 612 times.
✓ Branch 181 → 182 taken 34 times.
✓ Branch 181 → 187 taken 868 times.
902 } else if (compose && property == "font-size") {
187
1/1
✓ Branch 182 → 183 taken 34 times.
34 value = composeFontSize(value);
188 34 allowed = !value.isEmpty();
189
6/6
✓ Branch 187 → 188 taken 612 times.
✓ Branch 187 → 191 taken 256 times.
✓ Branch 189 → 190 taken 132 times.
✓ Branch 189 → 191 taken 480 times.
✓ Branch 192 → 193 taken 132 times.
✓ Branch 192 → 198 taken 736 times.
868 } else if (compose && property == "font-family") {
190
1/1
✓ Branch 193 → 194 taken 132 times.
132 value = composeFontFamily(value);
191 132 allowed = !value.isEmpty();
192
7/8
✓ Branch 198 → 199 taken 480 times.
✓ Branch 198 → 208 taken 256 times.
✓ Branch 200 → 201 taken 478 times.
✓ Branch 200 → 207 taken 2 times.
✓ Branch 202 → 203 taken 478 times.
✗ Branch 202 → 207 not taken.
✓ Branch 204 → 205 taken 461 times.
✓ Branch 204 → 207 taken 17 times.
1675 } else if (compose && (property == "margin-left" || property == "margin-right" ||
193
4/4
✓ Branch 206 → 207 taken 19 times.
✓ Branch 206 → 208 taken 442 times.
✓ Branch 209 → 210 taken 38 times.
✓ Branch 209 → 222 taken 698 times.
1675 property == "margin-top" || property == "margin-bottom")) {
194
1/1
✓ Branch 210 → 211 taken 38 times.
38 const int pixels = composeLengthPixels(value);
195 38 allowed = pixels >= 0;
196
2/2
✓ Branch 214 → 215 taken 38 times.
✓ Branch 215 → 216 taken 38 times.
38 value = QString::number(pixels) + QStringLiteral("px");
197
6/6
✓ Branch 222 → 223 taken 442 times.
✓ Branch 222 → 226 taken 256 times.
✓ Branch 224 → 225 taken 269 times.
✓ Branch 224 → 226 taken 173 times.
✓ Branch 227 → 228 taken 269 times.
✓ Branch 227 → 312 taken 429 times.
698 } else if (compose && property == "margin") {
198
2/2
✓ Branch 228 → 229 taken 269 times.
✓ Branch 232 → 233 taken 269 times.
538 const QStringList parts = value.simplified().split(QLatin1Char(' '));
199
5/10
✓ Branch 234 → 235 taken 8 times.
✓ Branch 234 → 248 taken 261 times.
✓ Branch 236 → 237 taken 8 times.
✗ Branch 236 → 248 not taken.
✓ Branch 245 → 246 taken 32 times.
✓ Branch 245 → 247 taken 8 times.
✗ Branch 409 → 410 not taken.
✗ Branch 409 → 411 not taken.
✗ Branch 418 → 419 not taken.
✗ Branch 418 → 420 not taken.
309 static const QStringList sides{"margin-top", "margin-right", "margin-bottom", "margin-left"};
200
5/10
✓ Branch 248 → 249 taken 8 times.
✓ Branch 248 → 262 taken 261 times.
✓ Branch 250 → 251 taken 8 times.
✗ Branch 250 → 262 not taken.
✓ Branch 259 → 260 taken 32 times.
✓ Branch 259 → 261 taken 8 times.
✗ Branch 422 → 423 not taken.
✗ Branch 422 → 424 not taken.
✗ Branch 432 → 433 not taken.
✗ Branch 432 → 434 not taken.
309 static const QList<QList<int>> pick{{0, 0, 0, 0}, {0, 1, 0, 1}, {0, 1, 2, 1}, {0, 1, 2, 3}};
201
3/6
✓ Branch 263 → 264 taken 269 times.
✗ Branch 263 → 267 not taken.
✓ Branch 265 → 266 taken 269 times.
✗ Branch 265 → 267 not taken.
✓ Branch 268 → 269 taken 269 times.
✗ Branch 268 → 309 not taken.
269 if (parts.size() >= 1 && parts.size() <= 4) {
202 269 QList<int> pixels;
203
5/7
✗ Branch 273 → 274 not taken.
✓ Branch 273 → 275 taken 332 times.
✗ Branch 274 → 276 not taken.
✓ Branch 276 → 277 taken 332 times.
✓ Branch 277 → 278 taken 332 times.
✓ Branch 281 → 271 taken 332 times.
✓ Branch 281 → 282 taken 269 times.
601 for (const QString &part : parts) pixels.append(composeLengthPixels(part == "auto" ? "0" : part));
204
2/2
✓ Branch 283 → 284 taken 256 times.
✓ Branch 283 → 303 taken 13 times.
269 if (!pixels.contains(-1)) {
205
2/2
✓ Branch 301 → 285 taken 1024 times.
✓ Branch 301 → 302 taken 256 times.
1280 for (int side = 0; side < 4; ++side)
206
4/4
✓ Branch 291 → 292 taken 1024 times.
✓ Branch 292 → 293 taken 1024 times.
✓ Branch 293 → 294 taken 1024 times.
✓ Branch 295 → 296 taken 1024 times.
1024 output.insert(sides[side], QString::number(pixels[pick[parts.size() - 1][side]]) + QStringLiteral("px"));
207 256 continue;
208 256 }
209 269 }
210 13 simplified = true;
211 13 continue;
212 269 }
213 // Authored colors/fonts/sizes/backgrounds remain theme-native and bounded.
214 // Only canonical keywords are serialized; CSS URLs/functions/escapes and
215 // arbitrary strings never become renderer capabilities.
216
7/7
✓ Branch 312 → 313 taken 295 times.
✓ Branch 312 → 316 taken 452 times.
✓ Branch 314 → 315 taken 294 times.
✓ Branch 314 → 316 taken 1 time.
✓ Branch 317 → 318 taken 294 times.
✓ Branch 317 → 319 taken 453 times.
✓ Branch 318 → 322 taken 294 times.
747 if (allowed && property != "direction") output.insert(property, value);
217
2/2
✓ Branch 319 → 320 taken 1 time.
✓ Branch 319 → 321 taken 452 times.
453 else if (allowed) continue;
218 452 else simplified = true;
219 29799 }
220 28501 QStringList declarationsOutput;
221
7/7
✓ Branch 337 → 338 taken 28501 times.
✓ Branch 342 → 343 taken 1323 times.
✓ Branch 343 → 344 taken 1323 times.
✓ Branch 344 → 345 taken 1323 times.
✓ Branch 348 → 349 taken 29824 times.
✓ Branch 350 → 339 taken 1323 times.
✓ Branch 350 → 351 taken 28501 times.
29824 for (auto it = output.cbegin(); it != output.cend(); ++it) declarationsOutput.append(it.key() + ':' + it.value());
222
1/1
✓ Branch 352 → 353 taken 28501 times.
28501 return declarationsOutput.join(';');
223 28525 }
224
225 28969 void start(void *context, const xmlChar *name, const xmlChar **attributes) {
226 28969 auto &state = *static_cast<State *>(context);
227
2/3
✓ Branch 2 → 3 taken 28969 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 28969 times.
28969 if (state.interrupted())
228 467 return;
229
5/6
✓ Branch 6 → 7 taken 28967 times.
✓ Branch 6 → 9 taken 2 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 28967 times.
✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 14 taken 28967 times.
57936 if (state.stack.size() >= NativeMailDocument::MaxDepth ||
230 28967 state.nodes.size() >= NativeMailDocument::MaxNodes) {
231
1/1
✓ Branch 12 → 13 taken 2 times.
2 state.stop();
232 2 return;
233 }
234
1/1
✓ Branch 15 → 16 taken 28967 times.
28967 const QString tag = QString::fromUtf8(reinterpret_cast<const char *>(name));
235 static const QSet<QString> suppressed{"head", "script", "style", "iframe",
236
5/10
✓ Branch 16 → 17 taken 13 times.
✓ Branch 16 → 33 taken 28954 times.
✓ Branch 18 → 19 taken 13 times.
✗ Branch 18 → 33 not taken.
✓ Branch 30 → 31 taken 91 times.
✓ Branch 30 → 32 taken 13 times.
✗ Branch 420 → 421 not taken.
✗ Branch 420 → 422 not taken.
✗ Branch 429 → 430 not taken.
✗ Branch 429 → 431 not taken.
29071 "object", "embed", "template"};
237
5/5
✓ Branch 33 → 34 taken 28967 times.
✓ Branch 34 → 35 taken 28683 times.
✓ Branch 34 → 37 taken 284 times.
✓ Branch 36 → 37 taken 181 times.
✓ Branch 36 → 38 taken 28502 times.
28967 const bool hidden = state.stack.last().suppressed || suppressed.contains(tag);
238
2/2
✓ Branch 39 → 40 taken 465 times.
✓ Branch 39 → 43 taken 28502 times.
28967 if (hidden) {
239
2/2
✓ Branch 40 → 41 taken 465 times.
✓ Branch 41 → 42 taken 465 times.
465 state.stack.append({state.stack.last().node, true});
240 465 return;
241 }
242 static const QSet<QString> allowed{
243 "html", "body", "div", "span", "p", "br", "hr", "h1", "h2",
244 "h3", "h4", "h5", "h6", "b", "strong", "i", "em", "u",
245 "s", "del", "blockquote", "ol", "ul", "li", "pre", "code", "table",
246
5/10
✓ Branch 43 → 44 taken 13 times.
✓ Branch 43 → 88 taken 28489 times.
✓ Branch 45 → 46 taken 13 times.
✗ Branch 45 → 88 not taken.
✓ Branch 85 → 86 taken 455 times.
✓ Branch 85 → 87 taken 13 times.
✗ Branch 434 → 435 not taken.
✗ Branch 434 → 436 not taken.
✗ Branch 443 → 444 not taken.
✗ Branch 443 → 445 not taken.
28970 "thead", "tbody", "tfoot", "tr", "td", "th", "a", "img"};
247
1/1
✓ Branch 88 → 89 taken 28502 times.
28502 Node node;
248 static const QSet<QString> blocks{"section", "article", "header", "footer",
249 "main", "nav", "aside", "figure",
250
5/10
✓ Branch 89 → 90 taken 13 times.
✓ Branch 89 → 111 taken 28489 times.
✓ Branch 91 → 92 taken 13 times.
✗ Branch 91 → 111 not taken.
✓ Branch 108 → 109 taken 156 times.
✓ Branch 108 → 110 taken 13 times.
✗ Branch 447 → 448 not taken.
✗ Branch 447 → 449 not taken.
✗ Branch 456 → 457 not taken.
✗ Branch 456 → 458 not taken.
28671 "figcaption", "details", "summary", "caption"};
251
2/2
✓ Branch 112 → 113 taken 28440 times.
✓ Branch 112 → 114 taken 62 times.
57066 node.tag = allowed.contains(tag) ? tag
252
2/2
✓ Branch 115 → 116 taken 12 times.
✓ Branch 115 → 119 taken 50 times.
74 : blocks.contains(tag) ? QStringLiteral("div")
253
4/4
✓ Branch 124 → 125 taken 50 times.
✓ Branch 124 → 126 taken 28452 times.
✓ Branch 126 → 127 taken 12 times.
✓ Branch 126 → 128 taken 28490 times.
57004 : QStringLiteral("span");
254
2/2
✓ Branch 129 → 130 taken 62 times.
✓ Branch 129 → 131 taken 28440 times.
28502 if (!allowed.contains(tag))
255 62 state.simplified = true;
256
2/2
✓ Branch 131 → 132 taken 28502 times.
✓ Branch 132 → 133 taken 28502 times.
28502 node.style = safeInlineStyle(attribute(attributes, "style"), state.simplified, node.direction, state.compose);
257
2/2
✓ Branch 136 → 137 taken 13373 times.
✓ Branch 136 → 161 taken 15129 times.
28502 if (state.compose) {
258
2/2
✓ Branch 137 → 138 taken 13373 times.
✓ Branch 141 → 142 taken 13373 times.
13373 const QStringList classes = attribute(attributes, "class").split(QLatin1Char(' '), Qt::SkipEmptyParts);
259
2/2
✓ Branch 149 → 150 taken 6 times.
✓ Branch 149 → 151 taken 13367 times.
13373 if (classes.contains(QStringLiteral("mailjd-signature"))) node.region = 1;
260
2/2
✓ Branch 157 → 158 taken 4 times.
✓ Branch 157 → 159 taken 13363 times.
13367 else if (classes.contains(QStringLiteral("mailjd-quote"))) node.region = 2;
261 13373 }
262
2/2
✓ Branch 161 → 162 taken 28502 times.
✓ Branch 162 → 163 taken 28502 times.
28502 const auto direction = attribute(attributes, "dir").toLower();
263
6/6
✓ Branch 165 → 166 taken 28478 times.
✓ Branch 165 → 168 taken 24 times.
✓ Branch 167 → 168 taken 26 times.
✓ Branch 167 → 169 taken 28452 times.
✓ Branch 170 → 171 taken 50 times.
✓ Branch 170 → 172 taken 28452 times.
28502 if (direction == "rtl" || direction == "ltr")
264 50 node.direction = direction;
265
2/2
✓ Branch 173 → 174 taken 4133 times.
✓ Branch 173 → 219 taken 24369 times.
28502 if (tag == "a") {
266
2/2
✓ Branch 174 → 175 taken 4133 times.
✓ Branch 175 → 176 taken 4133 times.
4133 const auto href = attribute(attributes, "href").trimmed();
267
1/1
✓ Branch 177 → 178 taken 4133 times.
4133 const QUrl url(href, QUrl::StrictMode);
268
2/2
✓ Branch 178 → 179 taken 4133 times.
✓ Branch 179 → 180 taken 4133 times.
4133 const auto scheme = url.scheme().toLower();
269
7/7
✓ Branch 182 → 183 taken 4132 times.
✓ Branch 182 → 200 taken 1 time.
✓ Branch 183 → 184 taken 4132 times.
✓ Branch 184 → 185 taken 4130 times.
✓ Branch 184 → 200 taken 2 times.
✓ Branch 186 → 187 taken 6 times.
✓ Branch 186 → 191 taken 4124 times.
12393 if (href.size() <= 2048 && url.isValid() &&
270
7/9
✓ Branch 188 → 189 taken 6 times.
✓ Branch 190 → 191 taken 1 time.
✓ Branch 190 → 199 taken 5 times.
✓ Branch 192 → 193 taken 4123 times.
✓ Branch 192 → 195 taken 2 times.
✓ Branch 205 → 206 taken 4104 times.
✓ Branch 205 → 211 taken 29 times.
✗ Branch 486 → 487 not taken.
✗ Branch 486 → 488 not taken.
12394 ((scheme == "mailto" && !url.path().isEmpty()) ||
271
9/11
✓ Branch 194 → 195 taken 4098 times.
✓ Branch 194 → 200 taken 25 times.
✓ Branch 196 → 197 taken 4100 times.
✓ Branch 198 → 199 taken 4099 times.
✓ Branch 198 → 200 taken 1 time.
✓ Branch 201 → 202 taken 4100 times.
✓ Branch 201 → 203 taken 33 times.
✓ Branch 203 → 204 taken 6 times.
✓ Branch 203 → 205 taken 4127 times.
✗ Branch 479 → 480 not taken.
✗ Branch 479 → 481 not taken.
16481 ((scheme == "http" || scheme == "https") && !url.host().isEmpty())))
272
1/1
✓ Branch 207 → 208 taken 4104 times.
4104 node.href = url.toString(QUrl::FullyEncoded);
273 else {
274
1/1
✓ Branch 211 → 212 taken 29 times.
29 node.tag = "span";
275
2/2
✓ Branch 213 → 214 taken 28 times.
✓ Branch 213 → 215 taken 1 time.
29 if (!href.isEmpty())
276 28 state.simplified = true;
277 }
278 4133 }
279
2/2
✓ Branch 220 → 221 taken 475 times.
✓ Branch 220 → 386 taken 28027 times.
28502 if (tag == "img") {
280
1/1
✓ Branch 221 → 222 taken 475 times.
475 node.tag = "span";
281
2/2
✓ Branch 222 → 223 taken 475 times.
✓ Branch 223 → 224 taken 475 times.
475 const QString source = attribute(attributes, "src").trimmed();
282
9/12
✓ Branch 225 → 226 taken 475 times.
✓ Branch 226 → 227 taken 475 times.
✓ Branch 227 → 228 taken 226 times.
✓ Branch 227 → 231 taken 249 times.
✓ Branch 229 → 230 taken 225 times.
✓ Branch 229 → 231 taken 1 time.
✓ Branch 232 → 233 taken 475 times.
✗ Branch 232 → 234 not taken.
✓ Branch 234 → 235 taken 225 times.
✓ Branch 234 → 255 taken 250 times.
✗ Branch 504 → 505 not taken.
✗ Branch 504 → 506 not taken.
475 if (source.startsWith("cid:", Qt::CaseInsensitive) && source.size() <= 2048) {
283
2/2
✓ Branch 235 → 236 taken 225 times.
✓ Branch 236 → 237 taken 225 times.
225 const QString id = canonicalContentId(source.mid(4));
284 225 bool valid = !id.isEmpty();
285
8/8
✓ Branch 242 → 243 taken 812 times.
✓ Branch 242 → 245 taken 1 time.
✓ Branch 244 → 245 taken 1 time.
✓ Branch 244 → 246 taken 811 times.
✓ Branch 247 → 248 taken 2 times.
✓ Branch 247 → 249 taken 811 times.
✓ Branch 250 → 241 taken 813 times.
✓ Branch 250 → 251 taken 225 times.
1038 for (QChar c : id) if (c.isSpace() || c.category() == QChar::Other_Control) valid = false;
286
2/2
✓ Branch 251 → 252 taken 222 times.
✓ Branch 251 → 253 taken 3 times.
225 if (valid) node.contentId = id;
287 225 }
288
1/1
✓ Branch 255 → 256 taken 475 times.
475 const QUrl remote(source, QUrl::StrictMode);
289 // S106-038: the bound is on the encoded URL, as NativeDocumentCache::get()
290 // checks it — a non-ASCII URL under 4096 characters but above 4096
291 // encoded bytes was cached and then never read back.
292
10/12
✓ Branch 259 → 260 taken 475 times.
✓ Branch 261 → 262 taken 474 times.
✓ Branch 261 → 279 taken 1 time.
✓ Branch 262 → 263 taken 474 times.
✓ Branch 263 → 264 taken 472 times.
✓ Branch 263 → 279 taken 2 times.
✓ Branch 267 → 268 taken 471 times.
✓ Branch 267 → 279 taken 1 time.
✓ Branch 290 → 291 taken 223 times.
✓ Branch 290 → 292 taken 252 times.
✗ Branch 532 → 533 not taken.
✗ Branch 532 → 534 not taken.
1422 if (source.size() <= 4096 && remote.toEncoded().size() <= 4096 && remote.isValid() &&
293
2/5
✓ Branch 265 → 266 taken 472 times.
✓ Branch 288 → 289 taken 475 times.
✗ Branch 288 → 290 not taken.
✗ Branch 527 → 528 not taken.
✗ Branch 527 → 529 not taken.
947 remote.userInfo().isEmpty() &&
294
17/25
✓ Branch 257 → 258 taken 475 times.
✗ Branch 257 → 279 not taken.
✓ Branch 269 → 270 taken 471 times.
✓ Branch 271 → 272 taken 223 times.
✓ Branch 271 → 279 taken 248 times.
✓ Branch 272 → 273 taken 223 times.
✓ Branch 274 → 275 taken 222 times.
✓ Branch 274 → 278 taken 1 time.
✓ Branch 275 → 276 taken 222 times.
✓ Branch 277 → 278 taken 222 times.
✗ Branch 277 → 279 not taken.
✓ Branch 280 → 281 taken 222 times.
✓ Branch 280 → 282 taken 253 times.
✓ Branch 282 → 283 taken 223 times.
✓ Branch 282 → 284 taken 252 times.
✓ Branch 284 → 285 taken 471 times.
✓ Branch 284 → 286 taken 4 times.
✓ Branch 286 → 287 taken 472 times.
✓ Branch 286 → 288 taken 3 times.
✗ Branch 512 → 513 not taken.
✗ Branch 512 → 514 not taken.
✗ Branch 517 → 518 not taken.
✗ Branch 517 → 519 not taken.
✗ Branch 522 → 523 not taken.
✗ Branch 522 → 524 not taken.
950 !remote.host().isEmpty() && (remote.scheme() == "http" || remote.scheme() == "https"))
295 223 node.remoteUrl = remote;
296
11/15
✓ Branch 292 → 293 taken 220 times.
✓ Branch 292 → 299 taken 255 times.
✓ Branch 296 → 297 taken 220 times.
✓ Branch 297 → 298 taken 4 times.
✓ Branch 297 → 299 taken 216 times.
✓ Branch 300 → 301 taken 220 times.
✓ Branch 300 → 302 taken 255 times.
✓ Branch 302 → 303 taken 220 times.
✓ Branch 302 → 304 taken 255 times.
✓ Branch 304 → 305 taken 4 times.
✓ Branch 304 → 346 taken 471 times.
✗ Branch 537 → 538 not taken.
✗ Branch 537 → 539 not taken.
✗ Branch 541 → 542 not taken.
✗ Branch 541 → 543 not taken.
695 if (state.compose && source.startsWith(QStringLiteral("data:image/"), Qt::CaseInsensitive)) {
297 6 static const QRegularExpression dataUrl(QStringLiteral(
298 "^data:(image/(?:png|jpeg|gif|webp));base64,([A-Za-z0-9+/=\\s]+)$"),
299
4/7
✓ Branch 305 → 306 taken 3 times.
✓ Branch 305 → 318 taken 1 time.
✓ Branch 307 → 308 taken 3 times.
✗ Branch 307 → 318 not taken.
✓ Branch 312 → 313 taken 3 times.
✗ Branch 552 → 553 not taken.
✗ Branch 552 → 554 not taken.
10 QRegularExpression::CaseInsensitiveOption);
300
1/1
✓ Branch 319 → 320 taken 4 times.
4 const auto match = dataUrl.match(source);
301
2/3
✓ Branch 320 → 321 taken 4 times.
✓ Branch 321 → 322 taken 4 times.
✗ Branch 321 → 344 not taken.
4 if (match.hasMatch()) {
302
3/3
✓ Branch 323 → 324 taken 4 times.
✓ Branch 324 → 325 taken 4 times.
✓ Branch 325 → 326 taken 4 times.
4 const QByteArray decoded = QByteArray::fromBase64(match.captured(2).toLatin1());
303
3/6
✓ Branch 329 → 330 taken 4 times.
✗ Branch 329 → 333 not taken.
✓ Branch 331 → 332 taken 4 times.
✗ Branch 331 → 333 not taken.
✓ Branch 334 → 335 taken 4 times.
✗ Branch 334 → 342 not taken.
4 if (!decoded.isEmpty() && decoded.size() <= NativeMailDocument::MaxDataImageBytes) {
304 4 node.imageData = decoded;
305
2/2
✓ Branch 336 → 337 taken 4 times.
✓ Branch 337 → 338 taken 4 times.
4 node.imageType = match.captured(1).toLower();
306 }
307 4 }
308 4 }
309
2/2
✓ Branch 346 → 347 taken 220 times.
✓ Branch 346 → 356 taken 255 times.
475 if (state.compose) {
310
2/2
✓ Branch 347 → 348 taken 220 times.
✓ Branch 348 → 349 taken 220 times.
220 node.imageWidth = qBound(0, attribute(attributes, "width").toInt(), 4096);
311
2/2
✓ Branch 351 → 352 taken 220 times.
✓ Branch 352 → 353 taken 220 times.
220 node.imageHeight = qBound(0, attribute(attributes, "height").toInt(), 4096);
312 }
313
1/1
✓ Branch 356 → 357 taken 475 times.
475 node.text = attribute(attributes, "alt");
314
6/6
✓ Branch 360 → 361 taken 411 times.
✓ Branch 360 → 363 taken 64 times.
✓ Branch 361 → 362 taken 208 times.
✓ Branch 361 → 363 taken 203 times.
✓ Branch 364 → 365 taken 208 times.
✓ Branch 364 → 372 taken 267 times.
475 if (node.text.isEmpty() && !state.compose)
315 208 node.text = QStringLiteral("[Image]");
316 475 state.textCharacters += node.text.size();
317
1/2
✗ Branch 373 → 374 not taken.
✓ Branch 373 → 376 taken 475 times.
475 if (state.textCharacters > 1024 * 1024) {
318 ✗ state.stop();
319 ✗ return;
320 }
321 475 state.simplified = true;
322 475 }
323
6/6
✓ Branch 387 → 388 taken 24435 times.
✓ Branch 387 → 390 taken 4067 times.
✓ Branch 389 → 390 taken 121 times.
✓ Branch 389 → 391 taken 24314 times.
✓ Branch 392 → 393 taken 4188 times.
✓ Branch 392 → 396 taken 24314 times.
28502 if (tag == "td" || tag == "th") {
324 8376 auto span = [&](const char *name) {
325
1/1
✓ Branch 2 → 3 taken 8376 times.
8376 const QString raw = attribute(attributes, name);
326
2/2
✓ Branch 4 → 5 taken 7329 times.
✓ Branch 4 → 6 taken 1047 times.
8376 if (raw.isEmpty())
327 7329 return 1;
328 1047 bool valid = false;
329
1/1
✓ Branch 6 → 7 taken 1047 times.
1047 const int value = raw.toInt(&valid);
330
4/4
✓ Branch 7 → 8 taken 1045 times.
✓ Branch 7 → 10 taken 2 times.
✓ Branch 8 → 9 taken 1041 times.
✓ Branch 8 → 10 taken 4 times.
1047 if (valid && value > 0)
331 1041 return value;
332 6 state.simplified = true;
333 6 return 1;
334 8376 };
335
1/1
✓ Branch 393 → 394 taken 4188 times.
4188 node.columns = span("colspan");
336
1/1
✓ Branch 394 → 395 taken 4188 times.
4188 node.rows = span("rowspan");
337 }
338
1/1
✓ Branch 396 → 397 taken 28502 times.
28502 const int parent = state.stack.last().node;
339 28502 const int next = state.nodes.size();
340
2/2
✓ Branch 398 → 399 taken 28502 times.
✓ Branch 399 → 400 taken 28502 times.
28502 state.nodes[parent].children.append(next);
341
1/1
✓ Branch 400 → 401 taken 28502 times.
28502 state.nodes.append(node);
342
1/1
✓ Branch 401 → 402 taken 28502 times.
28502 state.stack.append({next, false});
343 29006 }
344 28838 void end(void *context, const xmlChar *) {
345 28838 auto &state = *static_cast<State *>(context);
346
1/2
✓ Branch 3 → 4 taken 28838 times.
✗ Branch 3 → 5 not taken.
28838 if (state.stack.size() > 1)
347 28838 state.stack.removeLast();
348 28838 }
349 32596 void characters(void *context, const xmlChar *text, int length) {
350
1/1
✓ Branch 3 → 4 taken 32596 times.
32596 static_cast<State *>(context)->appendText(
351
1/1
✓ Branch 2 → 3 taken 32596 times.
65192 QString::fromUtf8(reinterpret_cast<const char *>(text), length));
352 32596 }
353 ✗ xmlParserInputPtr denyEntity(void *context, const xmlChar *, const xmlChar *) {
354 ✗ ++static_cast<State *>(context)->deniedEntities;
355 ✗ return nullptr;
356 }
357 12 void ignoreReport(void *, const char *, ...) {}
358
359 121 bool tableFits(const QVector<Node> &nodes, int table, int &remainingCells) {
360 121 QVector<int> rows;
361 ✗ std::function<void(int)> collect = [&](int id) {
362 1711 const auto &node = nodes[id];
363
2/2
✓ Branch 4 → 5 taken 1576 times.
✓ Branch 4 → 7 taken 135 times.
1711 if (node.tag == "tr") {
364 1576 rows.append(id);
365 1576 return;
366 }
367
4/6
✓ Branch 8 → 9 taken 121 times.
✓ Branch 8 → 11 taken 14 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 121 times.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 135 times.
135 if (node.tag == "table" && id != table)
368 ✗ return;
369
2/2
✓ Branch 20 → 16 taken 1590 times.
✓ Branch 20 → 21 taken 135 times.
1725 for (int child : node.children)
370
1/1
✓ Branch 17 → 18 taken 1590 times.
1590 collect(child);
371
1/1
✓ Branch 2 → 3 taken 121 times.
121 };
372
1/1
✓ Branch 3 → 4 taken 121 times.
121 collect(table);
373
2/2
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 7 taken 119 times.
121 if (rows.size() > 128)
374 2 return false;
375 119 std::array<std::array<bool, 32>, 128> occupied{};
376 119 int cells = 0;
377
2/2
✓ Branch 58 → 8 taken 386 times.
✓ Branch 58 → 59 taken 111 times.
497 for (int row = 0; row < rows.size(); ++row) {
378 386 int column = 0;
379
3/3
✓ Branch 8 → 9 taken 386 times.
✓ Branch 54 → 12 taken 678 times.
✓ Branch 54 → 55 taken 378 times.
1056 for (int id : nodes[rows[row]].children) {
380 678 const auto &cell = nodes[id];
381
4/6
✓ Branch 15 → 16 taken 119 times.
✓ Branch 15 → 19 taken 559 times.
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 119 times.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 678 times.
678 if (cell.tag != "td" && cell.tag != "th")
382 ✗ continue;
383
6/6
✓ Branch 24 → 25 taken 687 times.
✓ Branch 24 → 29 taken 3 times.
✓ Branch 27 → 28 taken 12 times.
✓ Branch 27 → 29 taken 675 times.
✓ Branch 30 → 23 taken 12 times.
✓ Branch 30 → 31 taken 678 times.
690 while (column < 32 && occupied[row][column])
384 12 ++column;
385
5/6
✓ Branch 31 → 32 taken 677 times.
✓ Branch 31 → 35 taken 1 time.
✓ Branch 32 → 33 taken 677 times.
✗ Branch 32 → 35 not taken.
✓ Branch 33 → 34 taken 674 times.
✓ Branch 33 → 35 taken 3 times.
678 if (cell.columns > 32 || cell.rows > 128 || column + cell.columns > 32 ||
386
2/2
✓ Branch 34 → 35 taken 1 time.
✓ Branch 34 → 36 taken 673 times.
674 row + cell.rows > 128)
387 8 return false;
388 673 cells += cell.columns * cell.rows;
389
4/4
✓ Branch 36 → 37 taken 672 times.
✓ Branch 36 → 38 taken 1 time.
✓ Branch 37 → 38 taken 1 time.
✓ Branch 37 → 39 taken 671 times.
673 if (cells > 2048 || cells > remainingCells)
390 2 return false;
391
2/2
✓ Branch 50 → 40 taken 936 times.
✓ Branch 50 → 51 taken 670 times.
1606 for (int r = row; r < row + cell.rows; ++r)
392
2/2
✓ Branch 48 → 41 taken 8885 times.
✓ Branch 48 → 49 taken 935 times.
9820 for (int c = column; c < column + cell.columns; ++c) {
393
2/2
✓ Branch 43 → 44 taken 1 time.
✓ Branch 43 → 45 taken 8884 times.
8885 if (occupied[r][c])
394 1 return false;
395 8884 occupied[r][c] = true;
396 }
397 670 column += cell.columns;
398 }
399 }
400 111 remainingCells -= cells;
401 111 return true;
402 121 }
403 } // namespace
404 590 NativeMailDocument NativeMailDocument::prepare(const QByteArray &input,
405 const std::atomic_bool *cancelled, Mode mode) {
406 590 NativeMailDocument result;
407
6/6
✓ Branch 2 → 3 taken 71 times.
✓ Branch 2 → 6 taken 519 times.
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 70 times.
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 9 taken 589 times.
590 if (cancelled && cancelled->load(std::memory_order_relaxed)) {
408 1 result.outcome = Outcome::Cancelled;
409 1 return result;
410 }
411
2/2
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 12 taken 587 times.
589 if (input.size() > MaxInputBytes) {
412 2 result.outcome = Outcome::Limited;
413 2 return result;
414 }
415
1/1
✓ Branch 12 → 13 taken 587 times.
587 State state;
416 587 state.cancelled = cancelled;
417 587 state.compose = mode == Mode::Compose;
418
5/6
✓ Branch 13 → 14 taken 206 times.
✓ Branch 13 → 21 taken 381 times.
✓ Branch 16 → 17 taken 195 times.
✓ Branch 16 → 20 taken 11 times.
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 195 times.
782 state.wordHtml = state.compose && (input.contains("MsoNormal") ||
419 195 input.contains("urn:schemas-microsoft-com:office:office"));
420 587 htmlSAXHandler sax{};
421 587 sax.startElement = start;
422 587 sax.endElement = end;
423 587 sax.characters = characters;
424 587 sax.cdataBlock = characters;
425 587 sax.resolveEntity = denyEntity;
426 587 sax.warning = ignoreReport;
427 587 sax.error = ignoreReport;
428 587 sax.fatalError = ignoreReport;
429 587 state.parser =
430
1/1
✓ Branch 22 → 23 taken 587 times.
587 htmlCreatePushParserCtxt(&sax, &state, nullptr, 0, nullptr, XML_CHAR_ENCODING_UTF8);
431
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 587 times.
587 if (!state.parser) {
432 ✗ result.outcome = Outcome::Failed;
433 ✗ return result;
434 }
435
1/1
✓ Branch 25 → 26 taken 587 times.
587 htmlCtxtUseOptions(state.parser, HTML_PARSE_RECOVER | HTML_PARSE_NONET | HTML_PARSE_NODEFDTD);
436
7/7
✓ Branch 33 → 34 taken 836 times.
✓ Branch 33 → 37 taken 586 times.
✓ Branch 34 → 35 taken 836 times.
✓ Branch 35 → 36 taken 835 times.
✓ Branch 35 → 37 taken 1 time.
✓ Branch 38 → 27 taken 835 times.
✓ Branch 38 → 39 taken 587 times.
1422 for (qsizetype offset = 0; offset < input.size() && !state.interrupted(); offset += 8192)
437
1/1
✓ Branch 30 → 31 taken 835 times.
835 htmlParseChunk(state.parser, input.constData() + offset,
438 1670 int(qMin<qsizetype>(8192, input.size() - offset)), 0);
439
3/3
✓ Branch 39 → 40 taken 587 times.
✓ Branch 40 → 41 taken 584 times.
✓ Branch 40 → 42 taken 3 times.
587 if (!state.interrupted())
440
1/1
✓ Branch 41 → 42 taken 584 times.
584 htmlParseChunk(state.parser, nullptr, 0, 1);
441 587 const int parseError = state.parser->errNo;
442
2/4
✓ Branch 42 → 43 taken 587 times.
✗ Branch 42 → 44 not taken.
✗ Branch 43 → 44 not taken.
✓ Branch 43 → 45 taken 587 times.
587 if (parseError == XML_ERR_RESOURCE_LIMIT || parseError == XML_ERR_NAME_TOO_LONG)
443 ✗ state.limited = true;
444
2/4
✓ Branch 45 → 46 taken 587 times.
✗ Branch 45 → 47 not taken.
✗ Branch 46 → 47 not taken.
✓ Branch 46 → 48 taken 587 times.
587 const bool failed = parseError == XML_ERR_INTERNAL_ERROR || parseError == XML_ERR_NO_MEMORY;
445
1/1
✓ Branch 49 → 50 taken 587 times.
587 htmlFreeParserCtxt(state.parser);
446 587 state.parser = nullptr;
447 587 result.nodes = state.nodes.size();
448 587 result.deniedEntities = state.deniedEntities;
449
1/2
✗ Branch 51 → 52 not taken.
✓ Branch 51 → 53 taken 587 times.
1174 result.outcome = state.wasCancelled ? Outcome::Cancelled
450
2/2
✓ Branch 53 → 54 taken 3 times.
✓ Branch 53 → 55 taken 584 times.
1171 : state.limited ? Outcome::Limited
451
1/2
✗ Branch 55 → 56 not taken.
✓ Branch 55 → 57 taken 584 times.
584 : failed ? Outcome::Failed
452 : Outcome::Ready;
453
2/2
✓ Branch 60 → 61 taken 3 times.
✓ Branch 60 → 62 taken 584 times.
587 if (result.outcome != Outcome::Ready)
454 3 return result;
455 584 QString output;
456 584 int remainingCells = 8192;
457 static const QSet<QString> blockTags{"p", "div", "li", "h1", "h2", "h3", "h4", "h5",
458
5/10
✓ Branch 62 → 63 taken 13 times.
✓ Branch 62 → 85 taken 571 times.
✓ Branch 64 → 65 taken 13 times.
✗ Branch 64 → 85 not taken.
✓ Branch 82 → 83 taken 169 times.
✓ Branch 82 → 84 taken 13 times.
✗ Branch 112 → 113 not taken.
✗ Branch 112 → 114 not taken.
✗ Branch 121 → 122 not taken.
✗ Branch 121 → 123 not taken.
766 "h6", "pre", "td", "th", "blockquote"};
459 ✗ std::function<void(int, bool, int, int)> emitNode = [&](int id, bool flatTable, int tableDepth, int region) {
460
3/3
✓ Branch 2 → 3 taken 52322 times.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 52321 times.
52322 if (state.interrupted())
461 28045 return;
462
1/1
✓ Branch 5 → 6 taken 52321 times.
52321 const auto &node = state.nodes[id];
463
2/2
✓ Branch 7 → 8 taken 28044 times.
✓ Branch 7 → 12 taken 24277 times.
52321 if (node.tag.isEmpty()) {
464
2/2
✓ Branch 8 → 9 taken 28044 times.
✓ Branch 9 → 10 taken 28044 times.
28044 output += node.text.toHtmlEscaped();
465 28044 return;
466 }
467 24277 QString tag = node.tag;
468
2/2
✓ Branch 14 → 15 taken 121 times.
✓ Branch 14 → 23 taken 24156 times.
24277 if (tag == "table") {
469 121 ++tableDepth;
470
5/7
✓ Branch 15 → 16 taken 121 times.
✗ Branch 15 → 19 not taken.
✓ Branch 16 → 17 taken 121 times.
✗ Branch 16 → 19 not taken.
✓ Branch 17 → 18 taken 121 times.
✓ Branch 18 → 19 taken 10 times.
✓ Branch 18 → 20 taken 111 times.
121 flatTable = flatTable || tableDepth > 3 || !tableFits(state.nodes, id, remainingCells);
471
2/2
✓ Branch 21 → 22 taken 10 times.
✓ Branch 21 → 23 taken 111 times.
121 if (flatTable)
472 10 state.simplified = true;
473 }
474
12/12
✓ Branch 23 → 24 taken 18341 times.
✓ Branch 23 → 37 taken 5936 times.
✓ Branch 25 → 26 taken 18339 times.
✓ Branch 25 → 36 taken 2 times.
✓ Branch 27 → 28 taken 18338 times.
✓ Branch 27 → 36 taken 1 time.
✓ Branch 29 → 30 taken 18336 times.
✓ Branch 29 → 36 taken 2 times.
✓ Branch 31 → 32 taken 18333 times.
✓ Branch 31 → 36 taken 3 times.
✓ Branch 33 → 34 taken 18330 times.
✓ Branch 33 → 36 taken 3 times.
60940 if (tableDepth == 0 && (tag == "thead" || tag == "tbody" || tag == "tfoot" || tag == "tr" ||
475
4/4
✓ Branch 35 → 36 taken 2 times.
✓ Branch 35 → 37 taken 18328 times.
✓ Branch 38 → 39 taken 13 times.
✓ Branch 38 → 40 taken 24264 times.
60940 tag == "td" || tag == "th")) {
476 13 flatTable = true;
477 13 state.simplified = true;
478 }
479
4/4
✓ Branch 40 → 41 taken 5090 times.
✓ Branch 40 → 52 taken 19187 times.
✓ Branch 42 → 43 taken 5080 times.
✓ Branch 42 → 51 taken 10 times.
34447 if (flatTable &&
480
10/10
✓ Branch 44 → 45 taken 5077 times.
✓ Branch 44 → 51 taken 3 times.
✓ Branch 46 → 47 taken 5075 times.
✓ Branch 46 → 51 taken 2 times.
✓ Branch 48 → 49 taken 5072 times.
✓ Branch 48 → 51 taken 3 times.
✓ Branch 50 → 51 taken 1329 times.
✓ Branch 50 → 52 taken 3743 times.
✓ Branch 53 → 54 taken 1347 times.
✓ Branch 53 → 55 taken 22930 times.
34447 (tag == "table" || tag == "thead" || tag == "tbody" || tag == "tfoot" || tag == "tr"))
481
1/1
✓ Branch 54 → 55 taken 1347 times.
1347 tag = "div";
482
8/8
✓ Branch 55 → 56 taken 5090 times.
✓ Branch 55 → 61 taken 19187 times.
✓ Branch 57 → 58 taken 1350 times.
✓ Branch 57 → 60 taken 3740 times.
✓ Branch 59 → 60 taken 3 times.
✓ Branch 59 → 61 taken 1347 times.
✓ Branch 62 → 63 taken 3743 times.
✓ Branch 62 → 64 taken 20534 times.
24277 if (flatTable && (tag == "td" || tag == "th"))
483
1/1
✓ Branch 63 → 64 taken 3743 times.
3743 tag = "span";
484
2/2
✓ Branch 65 → 66 taken 24277 times.
✓ Branch 66 → 67 taken 24277 times.
24277 output += '<' + tag;
485
2/2
✓ Branch 69 → 70 taken 51 times.
✓ Branch 69 → 77 taken 24226 times.
24277 if (!node.direction.isEmpty())
486
3/3
✓ Branch 71 → 72 taken 51 times.
✓ Branch 72 → 73 taken 51 times.
✓ Branch 73 → 74 taken 51 times.
51 output += " dir=\"" + node.direction + '"';
487 24277 QString style = node.style;
488
2/2
✓ Branch 78 → 79 taken 13310 times.
✓ Branch 78 → 156 taken 10967 times.
24277 if (state.compose) {
489
2/2
✓ Branch 80 → 81 taken 9 times.
✓ Branch 80 → 88 taken 13301 times.
13319 if (tag == "code") style = QStringLiteral("font-family:mailjd-code");
490 // A quote's gap comes from its paragraphs, not from the container.
491
2/2
✓ Branch 89 → 90 taken 17 times.
✓ Branch 89 → 105 taken 13293 times.
13310 if (tag == "blockquote")
492
4/5
✓ Branch 94 → 95 taken 17 times.
✗ Branch 94 → 96 not taken.
✓ Branch 97 → 98 taken 17 times.
✓ Branch 98 → 99 taken 17 times.
✓ Branch 99 → 100 taken 17 times.
17 style += QString(style.isEmpty() ? "" : ";") + QStringLiteral("margin-top:0px;margin-bottom:0px");
493
2/2
✓ Branch 105 → 106 taken 10 times.
✓ Branch 105 → 107 taken 13300 times.
13310 if (node.region > 0) region = node.region;
494 // Word HTML styles its paragraphs from the (dropped) head: margin 0.
495
8/9
✓ Branch 107 → 108 taken 120 times.
✓ Branch 107 → 114 taken 13190 times.
✓ Branch 109 → 110 taken 31 times.
✓ Branch 109 → 114 taken 89 times.
✓ Branch 111 → 112 taken 31 times.
✓ Branch 112 → 113 taken 31 times.
✗ Branch 112 → 114 not taken.
✓ Branch 115 → 116 taken 31 times.
✓ Branch 115 → 131 taken 13279 times.
13310 if (state.wordHtml && tag == "p" && !style.contains(QLatin1String("margin")))
496
4/5
✓ Branch 120 → 121 taken 31 times.
✗ Branch 120 → 122 not taken.
✓ Branch 123 → 124 taken 31 times.
✓ Branch 124 → 125 taken 31 times.
✓ Branch 125 → 126 taken 31 times.
31 style += QString(style.isEmpty() ? "" : ";") + QStringLiteral("margin-top:0px;margin-bottom:0px");
497 // Qt's importer turns -qt-user-state into the block's user state;
498 // the composer import reads the region back from it.
499
6/6
✓ Branch 131 → 132 taken 48 times.
✓ Branch 131 → 135 taken 13262 times.
✓ Branch 133 → 134 taken 38 times.
✓ Branch 133 → 135 taken 10 times.
✓ Branch 136 → 137 taken 38 times.
✓ Branch 136 → 156 taken 13272 times.
13310 if (region > 0 && blockTags.contains(tag))
500
7/7
✓ Branch 137 → 138 taken 38 times.
✓ Branch 142 → 143 taken 10 times.
✓ Branch 142 → 144 taken 28 times.
✓ Branch 145 → 146 taken 38 times.
✓ Branch 146 → 147 taken 38 times.
✓ Branch 147 → 148 taken 38 times.
✓ Branch 148 → 149 taken 38 times.
76 style += QString(style.isEmpty() ? "" : ";") + QStringLiteral("-qt-user-state:") + QString::number(region);
501 }
502
5/5
✓ Branch 157 → 158 taken 555 times.
✓ Branch 157 → 165 taken 23722 times.
✓ Branch 159 → 160 taken 555 times.
✓ Branch 160 → 161 taken 555 times.
✓ Branch 161 → 162 taken 555 times.
24277 if (!style.isEmpty()) output += " style=\"" + style + '"';
503
5/6
✓ Branch 166 → 167 taken 4104 times.
✓ Branch 166 → 170 taken 20173 times.
✓ Branch 168 → 169 taken 4104 times.
✗ Branch 168 → 170 not taken.
✓ Branch 171 → 172 taken 4104 times.
✓ Branch 171 → 181 taken 20173 times.
24277 if (tag == "a" && !node.href.isEmpty())
504
4/4
✓ Branch 173 → 174 taken 4104 times.
✓ Branch 174 → 175 taken 4104 times.
✓ Branch 175 → 176 taken 4104 times.
✓ Branch 176 → 177 taken 4104 times.
4104 output += " href=\"" + node.href.toHtmlEscaped() + '"';
505
8/8
✓ Branch 182 → 183 taken 23950 times.
✓ Branch 182 → 185 taken 327 times.
✓ Branch 184 → 185 taken 118 times.
✓ Branch 184 → 187 taken 23832 times.
✓ Branch 185 → 186 taken 16 times.
✓ Branch 185 → 187 taken 429 times.
✓ Branch 188 → 189 taken 16 times.
✓ Branch 188 → 198 taken 24261 times.
24277 if ((tag == "td" || tag == "th") && node.columns > 1)
506
4/4
✓ Branch 190 → 191 taken 16 times.
✓ Branch 191 → 192 taken 16 times.
✓ Branch 192 → 193 taken 16 times.
✓ Branch 193 → 194 taken 16 times.
16 output += " colspan=\"" + QString::number(node.columns) + '"';
507
8/8
✓ Branch 199 → 200 taken 23950 times.
✓ Branch 199 → 202 taken 327 times.
✓ Branch 201 → 202 taken 118 times.
✓ Branch 201 → 204 taken 23832 times.
✓ Branch 202 → 203 taken 16 times.
✓ Branch 202 → 204 taken 429 times.
✓ Branch 205 → 206 taken 16 times.
✓ Branch 205 → 215 taken 24261 times.
24277 if ((tag == "td" || tag == "th") && node.rows > 1)
508
4/4
✓ Branch 207 → 208 taken 16 times.
✓ Branch 208 → 209 taken 16 times.
✓ Branch 209 → 210 taken 16 times.
✓ Branch 210 → 211 taken 16 times.
16 output += " rowspan=\"" + QString::number(node.rows) + '"';
509
1/1
✓ Branch 216 → 217 taken 24277 times.
24277 output += '>';
510
11/11
✓ Branch 218 → 219 taken 24055 times.
✓ Branch 218 → 223 taken 222 times.
✓ Branch 219 → 220 taken 24055 times.
✓ Branch 220 → 221 taken 23832 times.
✓ Branch 220 → 223 taken 223 times.
✓ Branch 222 → 223 taken 4 times.
✓ Branch 222 → 226 taken 23828 times.
✓ Branch 224 → 225 taken 305 times.
✓ Branch 224 → 226 taken 144 times.
✓ Branch 227 → 228 taken 305 times.
✓ Branch 227 → 249 taken 23972 times.
24726 if ((!node.contentId.isEmpty() || !node.remoteUrl.isEmpty() || !node.imageData.isEmpty()) &&
511 449 result.imageReferences.size() < NativeMailDocument::MaxImageReferences) {
512
1/1
✓ Branch 233 → 234 taken 305 times.
610 const QString marker = QStringLiteral("<!--mailjd-native-image-%1-->").arg(result.imageReferences.size());
513 305 result.imageReferences.append({marker, node.contentId, node.remoteUrl, node.imageData, node.imageType,
514 758 state.compose ? node.text : QString(), node.imageWidth, node.imageHeight});
515
1/1
✓ Branch 246 → 247 taken 305 times.
305 output += marker;
516 305 }
517
11/11
✓ Branch 249 → 250 taken 13310 times.
✓ Branch 249 → 256 taken 10967 times.
✓ Branch 251 → 252 taken 13298 times.
✓ Branch 251 → 257 taken 12 times.
✓ Branch 252 → 253 taken 13298 times.
✓ Branch 253 → 254 taken 13094 times.
✓ Branch 253 → 257 taken 204 times.
✓ Branch 255 → 256 taken 13090 times.
✓ Branch 255 → 257 taken 4 times.
✓ Branch 258 → 259 taken 24057 times.
✓ Branch 258 → 263 taken 220 times.
24277 if (!(state.compose && (!node.contentId.isEmpty() || !node.remoteUrl.isEmpty() || !node.imageData.isEmpty())))
518
2/2
✓ Branch 259 → 260 taken 24057 times.
✓ Branch 260 → 261 taken 24057 times.
24057 output += node.text.toHtmlEscaped();
519
2/2
✓ Branch 269 → 265 taken 51616 times.
✓ Branch 269 → 270 taken 24277 times.
75893 for (int child : node.children)
520
1/1
✓ Branch 266 → 267 taken 51616 times.
51616 emitNode(child, flatTable, tableDepth, region);
521
6/6
✓ Branch 271 → 272 taken 23897 times.
✓ Branch 271 → 275 taken 380 times.
✓ Branch 273 → 274 taken 23896 times.
✓ Branch 273 → 275 taken 1 time.
✓ Branch 276 → 277 taken 23896 times.
✓ Branch 276 → 284 taken 381 times.
24277 if (tag != "br" && tag != "hr")
522
3/3
✓ Branch 278 → 279 taken 23896 times.
✓ Branch 279 → 280 taken 23896 times.
✓ Branch 280 → 281 taken 23896 times.
23896 output += "</" + tag + '>';
523
8/8
✓ Branch 284 → 285 taken 5090 times.
✓ Branch 284 → 290 taken 19187 times.
✓ Branch 286 → 287 taken 1350 times.
✓ Branch 286 → 289 taken 3740 times.
✓ Branch 288 → 289 taken 3 times.
✓ Branch 288 → 290 taken 1347 times.
✓ Branch 291 → 292 taken 3743 times.
✓ Branch 291 → 293 taken 20534 times.
24277 if (flatTable && (node.tag == "td" || node.tag == "th"))
524
1/1
✓ Branch 292 → 293 taken 3743 times.
3743 output += " | ";
525
4/16
✓ Branch 85 → 86 taken 584 times.
✓ Branch 241 → 242 taken 148 times.
✓ Branch 241 → 243 taken 157 times.
✓ Branch 244 → 245 taken 305 times.
✗ Branch 403 → 404 not taken.
✗ Branch 403 → 405 not taken.
✗ Branch 406 → 407 not taken.
✗ Branch 406 → 408 not taken.
✗ Branch 409 → 410 not taken.
✗ Branch 409 → 411 not taken.
✗ Branch 412 → 413 not taken.
✗ Branch 412 → 414 not taken.
✗ Branch 415 → 416 not taken.
✗ Branch 415 → 417 not taken.
✗ Branch 418 → 419 not taken.
✗ Branch 418 → 420 not taken.
25166 };
526
5/5
✓ Branch 86 → 87 taken 584 times.
✓ Branch 87 → 88 taken 584 times.
✓ Branch 88 → 89 taken 584 times.
✓ Branch 94 → 90 taken 706 times.
✓ Branch 94 → 95 taken 584 times.
1290 for (int child : state.nodes[0].children)
527
1/1
✓ Branch 91 → 92 taken 706 times.
706 emitNode(child, false, 0, 0);
528
3/3
✓ Branch 95 → 96 taken 584 times.
✓ Branch 96 → 97 taken 1 time.
✓ Branch 96 → 102 taken 583 times.
584 if (state.interrupted()) {
529
1/2
✓ Branch 97 → 98 taken 1 time.
✗ Branch 97 → 99 not taken.
1 result.outcome = state.wasCancelled ? Outcome::Cancelled : Outcome::Limited;
530
1/1
✓ Branch 100 → 101 taken 1 time.
1 result.imageReferences.clear();
531 1 return result;
532 }
533 583 result.html = output;
534 583 result.simplified = state.simplified;
535 583 return result;
536 600 }
537