diff options
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Draw the diffstat bars as SVG
The bars were cells spanning up to 1000 columns of a nested table, which kept them free of inline styles under a strict CSP but broke the HTML table model, 18 errors on a commit page, and left Firefox's longest bar short of the full width. An SVG canvas sized by its attributes is just as safe under a CSP, validates, and draws every bar exactly to scale.
-rw-r--r--assets/cgit.css18
-rw-r--r--source/ui-diff.c25
-rwxr-xr-xtests/t0106-commit.sh6
3 files changed, 21 insertions, 28 deletions
diff --git a/assets/cgit.css b/assets/cgit.css
index af7ea3d..de772e4 100644
--- a/assets/cgit.css
+++ b/assets/cgit.css
@@ -966,24 +966,18 @@ div#cgit table.diffstat td.graph {
vertical-align: middle;
}
-div#cgit table.diffstat td.graph table {
- border: none;
- table-layout: fixed;
+div#cgit table.diffstat td.graph svg {
+ display: block;
width: 100%;
-}
-
-div#cgit table.diffstat td.graph td {
- padding: 0;
- border: 0;
height: 7pt;
}
-div#cgit table.diffstat td.graph td.add {
- background-color: var(--graph-add);
+div#cgit table.diffstat td.graph rect.add {
+ fill: var(--graph-add);
}
-div#cgit table.diffstat td.graph td.rem {
- background-color: var(--graph-rem);
+div#cgit table.diffstat td.graph rect.rem {
+ fill: var(--graph-rem);
}
div#cgit div.diffstat-summary {
diff --git a/source/ui-diff.c b/source/ui-diff.c
index 2da44c4..8d8e680 100644
--- a/source/ui-diff.c
+++ b/source/ui-diff.c
@@ -73,17 +73,6 @@ static void release_bodies(void)
strbuf_release(&items[i].body);
}
-/*
- * One bar segment of the per-file diffstat graph. The bar is a fixed-layout
- * table whose row always spans 1000 columns, so a segment takes its width
- * through colspan rather than an inline style a CSP would have to allow.
- */
-static void print_graph_cell(const char *class, int span)
-{
- if (span > 0)
- htmlf("<td class='%s' colspan='%d'></td>", class, span);
-}
-
static void print_fileinfo(struct fileinfo *info)
{
int scale = max_changes ? max_changes : 1;
@@ -152,13 +141,17 @@ static void print_fileinfo(struct fileinfo *info)
}
htmlf("%u", info->added + info->removed);
html("</td><td class='graph'>");
- html("<table><tr>");
add_span = (int)(info->added * 1000.0 / scale + 0.5);
rem_span = (int)(info->removed * 1000.0 / scale + 0.5);
- print_graph_cell("add", add_span);
- print_graph_cell("rem", rem_span);
- print_graph_cell("none", 1000 - add_span - rem_span);
- html("</tr></table></td></tr>\n");
+ // The bar is drawn on a canvas 1000 units wide that the cell stretches,
+ // so each segment takes its width from an attribute rather than from an
+ // inline style a CSP would have to allow.
+ html("<svg viewBox='0 0 1000 1' preserveAspectRatio='none' aria-hidden='true'>");
+ if (add_span > 0)
+ htmlf("<rect class='add' width='%d' height='1'/>", add_span);
+ if (rem_span > 0)
+ htmlf("<rect class='rem' x='%d' width='%d' height='1'/>", add_span, rem_span);
+ html("</svg></td></tr>\n");
}
/*
diff --git a/tests/t0106-commit.sh b/tests/t0106-commit.sh
index 2b924a0..3b50c2b 100755
--- a/tests/t0106-commit.sh
+++ b/tests/t0106-commit.sh
@@ -22,6 +22,12 @@ test_expect_success 'find diff summary' '
grep "1 file changed, 1 insertion, 0 deletions" tmp
'
+# A strict CSP refuses an inline style, so the bar takes its widths from SVG
+# attributes, which also leave the page valid.
+test_expect_success 'the diffstat bar is an SVG sized by its attributes' '
+ grep "<td class=.graph.><svg viewBox=.0 0 1000 1. preserveAspectRatio=.none. aria-hidden=.true.><rect class=.add. width=.1000. height=.1./></svg></td>" tmp
+'
+
test_expect_success 'get root commit' '
root=$(cd repos/foo && git rev-list --reverse HEAD | head -1) &&
cgit_url "foo/commit&id=$root" >tmp &&