element-hq / element-hq/element-web
Download fails if Element Web is inside an iframe
- Dominant language
- TypeScript
- Stars
- 13.5k
- Forks
- 2.8k
- PR merge metrics
- PR metrics pending
Description
### Steps to reproduce
1. Have your Element Web instance inside an iframe (please note the instance of Element Web is hosted in another subdomain than the subdomain of the parent's instance)
2. Try to download a file from a room
3. Nothing happens
### Outcome
#### What did you expect?
A download prompt popping.
#### What happened instead?
Nothing. In fact there is a fetch event triggered in the child window, but it doesn't propagate to the parent window.
I managed to override the fetch event in the child window in order to dispatch an event in the parentWindow :
```
initFetchEvent: function(selector) {
// override fetch method of iframe in order to be able to listen to AJAX calls made from the instance of the fetch API used by Element Web
const iframeWindow = document.querySelector(selector).contentWindow;
let mousePosX = 0;
let mousePosY = 0;
iframeWindow.document.addEventListener("mousemove", e => {
mousePosX = e.clientX;
mousePosY = e.clientY;
});
(function(riotWindow, fetch) {
if (typeof fetch !== 'function') return;
riotWindow.fetch = function() {
const downloadLinkElement = iframeWindow.document.elementFromPoint(mousePosX, mousePosY);
const result = fetch.apply(this, arguments);
riotWindow.parent.document.dispatchEvent(new CustomEvent("riotFetchRequest", {
detail: {endpoint: arguments[0], downloadLinkElement}
}));
return result;
}
}(iframeWindow, iframeWindow.fetch));
},
```
and then intercept it :
```
initFetchListener: function() {
// GOFAST-8052 - make download button work inside the iframe
document.addEventListener("riotFetchRequest", ({detail}) => {
if(detail.endpoint.includes("media/r0/download")) {
this.generateDownloadLink(detail.endpoint);
// there are two download link elements we have to set
const downloadLinkHoverable = detail.downloadLinkElement.closest(".mx_EventTile").querySelector(".mx_MessageActionBar_downloadButton");
const downloadLinkElement = detail.downloadLinkElement.closest(".mx_EventTile").querySelector(".mx_MFileBody");
[downloadLinkHoverable, downloadLinkElement].forEach((el) => {
el.addEventListener("click", e => {
e.preventDefault();
this.generateDownloadLink(detail.endpoint);
});
});
}
});
},
generateDownloadLink: function(endpoint) {
const link = document.createElement('a');
link.setAttribute("href", endpoint);
link.setAttribute("target", "_blank");
link.setAttribute('download', "download");
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
```
and it actually works.
But if someday one selector changes, the endpoint changes or the method used to fetch the file, it will not work anymore. This makes this workaround very temporary and flimsy.
This should work natively without having to make such overcomplicated override.
### Operating system
Fedora Workstation 36 (also tested with Arch Linux and Windows 10)
### Browser information
Firefox 102 (also tested with Chrome 105)
### URL for webapp
1.9.5
### Application version
1.9.5
### Homeserver
Synapse 1.47
### Will you send logs?
Yes
Contributor guide
Assessment
This issue has not been assessed yet.