diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss
index acc0066572..1de668985f 100644
--- a/res/css/views/rooms/_EventTile.scss
+++ b/res/css/views/rooms/_EventTile.scss
@@ -450,6 +450,15 @@ $left-gutter: 64px;
pre {
border: 1px solid transparent;
}
+
+ // selector wrongly applies to pill avatars but those have explicit width/height passed at a higher specificity
+ &.markdown-body img {
+ // the image will have max-width and max-height applied during sanitization
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ object-position: left top;
+ }
}
.mx_EventTile_clamp {
diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx
index f6c045714a..0b94cfd891 100644
--- a/src/HtmlUtils.tsx
+++ b/src/HtmlUtils.tsx
@@ -207,8 +207,12 @@ const transformTags: IExtendedSanitizeOptions["transformTags"] = { // custom to
return { tagName, attribs: {} };
}
- const width = Number(attribs.width) || 800;
- const height = Number(attribs.height) || 600;
+ const width = Math.min(Number(attribs.width) || 800, 800);
+ const height = Math.min(Number(attribs.height) || 600, 600);
+ // specify width/height as max values instead of absolute ones to allow object-fit to do its thing
+ // we only allow our own styles for this tag so overwrite the attribute
+ attribs.style = `max-width: ${width}px; max-height: ${height}px;`;
+
attribs.src = mediaFromMxc(src).getThumbnailOfSourceHttp(width, height);
return { tagName, attribs };
},
@@ -223,9 +227,12 @@ const transformTags: IExtendedSanitizeOptions["transformTags"] = { // custom to
return { tagName, attribs };
},
'*': function(tagName: string, attribs: sanitizeHtml.Attributes) {
- // Delete any style previously assigned, style is an allowedTag for font and span
- // because attributes are stripped after transforming
- delete attribs.style;
+ // Delete any style previously assigned, style is an allowedTag for font, span & img,
+ // because attributes are stripped after transforming.
+ // For img this is trusted as it is generated wholly within the img transformation method.
+ if (tagName !== "img") {
+ delete attribs.style;
+ }
// Sanitise and transform data-mx-color and data-mx-bg-color to their CSS
// equivalents
@@ -249,7 +256,7 @@ const transformTags: IExtendedSanitizeOptions["transformTags"] = { // custom to
});
if (style) {
- attribs.style = style;
+ attribs.style = style + (attribs.style || "");
}
return { tagName, attribs };
@@ -266,12 +273,15 @@ const sanitizeHtmlParams: IExtendedSanitizeOptions = {
'details', 'summary',
],
allowedAttributes: {
+ // attribute sanitization happens after transformations, so we have to accept `style` for font, span & img
+ // but strip during the transformation.
// custom ones first:
font: ['color', 'data-mx-bg-color', 'data-mx-color', 'style'], // custom to matrix
span: ['data-mx-maths', 'data-mx-bg-color', 'data-mx-color', 'data-mx-spoiler', 'style'], // custom to matrix
div: ['data-mx-maths'],
a: ['href', 'name', 'target', 'rel'], // remote target: custom to matrix
- img: ['src', 'width', 'height', 'alt', 'title'],
+ // img tags also accept width/height, we just map those to max-width & max-height during transformation
+ img: ['src', 'alt', 'title', 'style'],
ol: ['start'],
code: ['class'], // We don't actually allow all classes, we filter them in transformTags
},
diff --git a/test/components/structures/GroupView-test.js b/test/components/structures/GroupView-test.js
index 2fbea5ce7c..9d46b2fe71 100644
--- a/test/components/structures/GroupView-test.js
+++ b/test/components/structures/GroupView-test.js
@@ -264,7 +264,8 @@ describe('GroupView', function() {
const imgSrc = "https://my.home.server/_matrix/media/r0/thumbnail/someimageurl" +
"?width=800&height=600&method=scale";
- expect(longDescElement.innerHTML).toContain('');
+ expect(longDescElement.innerHTML).toContain('');
});
httpBackend