0
votes

The idea is quite simple in concept: I would like to create a userscript that will let me press a button and save something on the page(most commonly and problematically images). Note: A userscript is a script that is injected client-side(by browser extensions such as Tampermonkey and Greasemonkey) and is used to add functionality to a site.

To do so I merely need to call the saveAs() function and pass it the data.

The question then becomes how to I obtain the data.

Most approaches I've seen run into the situation where the resource is not of the same domain as the script perhaps?(not sure how this works).

Now, Tampermonkey(and Greasemonkey) have created a function to deal with this problem specifically - GM_XMLHTTPRequest, which can circumvent the need for proper CORS headers.

This however creates another request to the server, for a file that has already been downloaded.

My question is: Is there a way to not have to send secondary requests to the server?


Here is a chronicle of my efforts:

From what research I've managed to do, you can create a canvas and draw the image in there. However this "taints" the canvas, preventing it from running functions that extract that data(such as .toBlob() or .toDataURL()).
CORS offers 2 mechanisms as far as I understand it: Setting the proper HTTP headers, which requires control of the server, and a special attribute that can be put on HTML elements: crossorigin
I tried adding this property post-load and it won't work, you still get a tainted canvas.
Tampermonkey offers several different options on when to run the script. So the next idea was to run when the DOM is loaded, but the resources haven't yet been fetched. It seems the earliest this is possible is document-end(earlier the getElementById call returns null). However this currently returns an error when loading the image on the page(before any other additional code is run): Image from origin '...' has been blocked from loading by Cross-Origin Resource Sharing policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '...' is therefore not allowed access.
There's also the --disable-web-security flag in Chrome, but I'd rather not go there.

1
So your image data is not on he same domain as your web page data? Where is your image origin in relation to your web domain? - Binvention
If you just want to save a particular image as a download to something like jpg or png in the browser, I've done that. No need to call the server again, and not sure why CORS would be an issue. If that's the case, I can provide some tips. - Will
@Binvention I have no idea how it works with userscripts, but all errors relate to violations of the same-origin policy. The web page is at page.com, and the images are at images.page.com @Will Yes, please. If they apply in this case, any tip would be welcome. - martixy
Yes that would interfere with cross origin because sub domains are not considered the same. For example example.com is not the same domain as image.example.com however example.com is the same as example.com/images as far as cross domain security is concerned the solution for that is adding the headers your errors are referring to your image headers before they leave the server - Binvention
So you would add the header 'Access-Control-Allow-Origin'='yourpagedomainhere' to your image headers - Binvention

1 Answers

2
votes

No, there is no way to do it without a new request to the server.

When the first request is made, the image is marked as unsafe by the browser, and will then block a few features, like canvas' toDataURL, getImageData or toBlob, or in case of audio files, AudioContext's createMediaElementSource and AnalyserNode's methods and probably some others.

There is nothing you can do to circumvent this security, once it's marked as unsafe, it is unsafe. You then have to make a new request to the server to get a new file from the server in safe way this time.

Commonly, you would just set the crossOrigin attribute on the media element before doing the request, and after the server has been properly configured to answer to such requests.

Now in your case, it seems clear that you can't configure any server where your script will be used on.

But as you noticed, extensions such as GreaseMonkey or TamperMonkey have access to more features than basic javascript ran from a webpage. In these features, there is one allowing your browser to be less careful about such cross-origin requests, and this is what the GM_xmlhttpRequest method does.

But once again, even extensions don't have enough power to unmark non-safe media.

You must perform a new request, using their less secured way.