Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Current Opera + HTTP = download popup blocked #95

Closed
TexKiller opened this issue Apr 12, 2019 · 15 comments
Closed

Current Opera + HTTP = download popup blocked #95

TexKiller opened this issue Apr 12, 2019 · 15 comments

Comments

@TexKiller
Copy link
Contributor

TexKiller commented Apr 12, 2019

It seems the current version of Opera blocks the popup with the download link, even if StreamSaver.js is called from an user interaction.

The mitm.html popup is opened normally, but when it is time to open the popup with the download link it is blocked.

This does not happen on version 40 of the Opera browser, where the download is started as it should.

@TexKiller
Copy link
Contributor Author

I think this might be solved if we use the same popup to start the service worker and to start the download. Doing that would also reduce the number of popups that need to be opened for the download to start, both on HTTP and on background pages on Firefox.

@jimmywarting
Copy link
Owner

jimmywarting commented Apr 12, 2019

It seems the current version of Opera blocks the popup with the download link, even if StreamSaver.js is called from an user interaction.

The mitm.html popup is opened normally, but when it is time to open the popup with the download link it is blocked.

uh? had to read that 2,3 times... if the mitm.html opens just fine it should work as expected? in the current version the download happens using a hidden iframe on the main thread so there shouldn't be any popup blocker?

Will try to reproduce quickly, Maybe work on a fix tomorrow

edit apparently the download happen using popup also 😕

@TexKiller
Copy link
Contributor Author

TexKiller commented Apr 13, 2019

The download happens on a popup because the top page is insecure. Chrome doesn't allow a request from the <iframe> to be intercepted by the Service Worker.

If you try to start the download from inside the <iframe> on HTTP on Chrome, the 404 page is loaded inside it.

I think this is a good opportunity to start reusing the same popup, since there is another situation where it would be useful (Web Extension's background page on Firefox).

@jimmywarting
Copy link
Owner

The best solution is to re-use the popup. currently i see two solutions

  • Should we create a download link (iframe) inside the popup
  • or should we navigate to the download link
    popup.location.href = evt.data.download

@TexKiller
Copy link
Contributor Author

I don't know which one is best, but we should see if they interfere with the immortal Service Worker bug on Chrome and Opera. If there is a way to use the same popup and still have an immortal Service Worker, that would be best. :)

@jimmywarting
Copy link
Owner

jimmywarting commented Apr 14, 2019

The best thing maybe would just be to embrace the should allow to go idle thingy and transfer the readableStream directly to the download so no postMessage & pinging back and forth has to happen.

Would maybe also like to see the ping.html & ping.js go away to reduce nth of files in the feature.
off-topic question: what purpose dose ping.html have? can that not be done from mitm.html?


I have tried a prof-of-concept version by doing popup.location.href = evt.data.download and after a short timeout close the popup - so far i have not encounter any bugs and there is no popup blocking happening in either chrome or opera

@TexKiller
Copy link
Contributor Author

TexKiller commented Apr 14, 2019

The best thing maybe would just be to embrace the should allow to go idle thingy and transfer the readableStream directly to the download so no postMessage & pinging back and forth has to happen.

Sure, but for that we would have to have a way to start the Service Worker again when needed. On secure contexts we can do that from the iframe, so it shouldn't matter much, but on HTTP we would need to open another popup just to start the Service Worker.

Maybe we can keep a message port open until all the streams are closed, at which point we close the port, stop keeping the SW alive and require another mitm.html to register the Service Worker again. So we allow it to go iddle when not used, but while it is in use we avoid registering it again.

Would maybe also like to see the ping.html & ping.js go away to reduce nth of files in the feature.
off-topic question: what purpose dose ping.html have? can that not be done from mitm.html?

ping.html's only function is to start the Shared Worker and to communicate with it. I guess we could do a big if on mitm.html so it does this or its usual thing, as long as we establish a condition for that.

I have tried a prof-of-concept version by doing popup.location.href = evt.data.download and after a short timeout close the popup - so far i have not encounter any bugs and there is no popup blocking happening in either chrome or opera

Sounds fine. However, it would be very bad if we were to close this popup with a 10s timeout. That would be 10 seconds of a blank window open.

@jimmywarting
Copy link
Owner

jimmywarting commented Apr 20, 2019

Found out that you can't close the popup or iframe directly after you send the "download started" postMessage.
it happens almost immediately. And the StreamSaver is asynchronous. Meaning: you can open a downloadUrl and then close it. The "save as" dialog will not appear until after you have written some amount of data. "Torrent" and "write as" you type fails to bring up the saveAs dialog

What do you think is the best solution for that?

  • close the popup when stream is completed? [no good if small and finish fast]
  • close the popup after x bytes have been written? [no good if small and finish fast]
  • build some download progress interface in the popup?

even if you wait for a few bytes or for it to finish you still have to wait until the user hits the save button. think we should just leave the download iframe stay open and never close it (what harm dose a hidden blank iframe do? it doesn't run any js or have any DOM) but what should we do about the popup?

@TexKiller
Copy link
Contributor Author

TexKiller commented Apr 21, 2019

Well, shit. I hadn't thought about that use case.

I guess one possibility would be to figure out the conditions for triggering the download on each browser and only open the popup/start the download when those conditions are met. (e.g.: lets imagine Chrome only does it after 1024 bytes written or the stream is closed, then we wait for one of those conditions to happen and then start the download - by the way, this is made up, I don't actually know if those are Chrome's conditions).

I think anything else would either modify the download's content or leave a popup open for (possibly) a long time.

Another idea would be to try inserting a lot of filler headers on the response to try and trigger the download from those bytes instead of the download body's bytes. If this works the download would always be instantaneous, no matter the size or the time it takes to write the first byte.

@jimmywarting
Copy link
Owner

jimmywarting commented Apr 21, 2019

triggering the download on each browser and only open the popup/start the download when those conditions are met.

if we don't trigger the download right away (on user interaction) then we are back on popup being blocked.

We can't just close the popup after 1024 bytes has been written. We must also wait for the user to pick a path, set filename and hit save. (which we can't detect)

one possible solution is to

  1. Open the initial pooup that installs sw and transfair the DataChannel.
  2. sw respond back with a download path.
  3. we then close the initial popup. We then try to open up a new download-popup and assign the new popup to myWindow
    then the download-popup (myWindow) will automatically be closed after user save the file. but then we are maybe also back to being blocked by the popup blocker.
  4. if the myWindow = open(...) is null then we say to the initial mitm popup to open or redirect to the download url. otherwise the new popup was a successfully opened and then we can close the initial mitm-popup with scripting. and the download-popup will be closed after the user save it.

@jimmywarting
Copy link
Owner

jimmywarting commented Apr 21, 2019

found the best way is to redirect the main window, but only if the page is insecure and needs popups to the download url, in other cases we use hidden iframes.
but also only redirect the main thread when x bytes have been written so chrome knows it should download something and continue showing the webpage it's on and still be able to open up new connections

@jimmywarting
Copy link
Owner

Would it be better to use
https://developer.chrome.com/extensions/downloads
instead of chrome.tabs.create?

@TexKiller
Copy link
Contributor Author

TexKiller commented Apr 21, 2019

Well, we can always just buffer the first bytes without opening the mitm.html popup, and only open it when we have enough bytes to write, then we register the SW and start the download (to avoid the popup blocker). Edit: now that I think about it, if we wait we might no longer be able to open the popup. We can shift the id creation to StreamSaver.js if we want to have the download link before receiving the message from the SW, that way we can open both popups at once.

I don't know if the downloads API will work with the SW interception. We can test it.

Redirecting the main window will still close open connections, right? If it is possible to avoid it, I think we should.

What happens when we close the popup before the user selects the destination folder? On Firefox the download proceeds as it should, but I think my Chrome was always configured to save on the Downloads folder by default.

@TexKiller
Copy link
Contributor Author

If we can force the download to start without writing any byte by messing with the headers, that would be ideal.

@jimmywarting
Copy link
Owner

jimmywarting commented Apr 23, 2019

I think i may have found a hack do not having to open any further popups at all after the service worker have been installed 😁 🤪

Edit Damm it didn't work, but god what a awful hack that would have been. Don't think you even would want to hear about it, haha - this hole expose secureOnly specification is a pain in the butt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants