Loading and displaying remote HTTP images in an extension (original) (raw)
Hello everyone.
My extension QR Lite has a feature where the user opens the context menu on an image and choose “Scan QR code in Image”, the extension will load the target image via its URL in a popup window, read the image’s data and make an attempt to decode QR code.
The feature works well for images loaded though HTTPS, but it fails to load HTTP-only images. The browser seems to consider it Mixed Content, and always attempt to upgrade to HTTPS.
You can test it out by installing the extension, then open the following URL, right-click the image and choose “Scan QR Code in Image”.
(It’s not an image of an QR Code but it could still serve our purpose. Hard to find an HTTP-only image on the internet these days.)
http://www.vulnweb.com/acunetix-logo.png
Normally you would see the target image being displayed in the popup window but in this case the image would fail to load. If you inspect the popup it should say in the console:
Mixed Content: Upgrading insecure display request ‘http://www.vulnweb.com/acunetix-logo.png’ to use ‘https’
According to this MDN page, mixed content will either be upgraded or blocked, and there seems to be no other way around.
Another MDN page suggests that extensions can opt out of the insecure request upgrading behavior by using a CSP that does not include upgrade-insecure-requests. But my extension already does that and HTTP requests are still being upgraded.
Is there a way to make HTTP-only images work, or is it a dead end?
I want to support HTTP-only images because I can still think of valid use cases for it.
Any help would be appreciated.
I think you should be able to fetch it using fetch.
At least a quick proof of concept suggests it works - execute this in extension page:
document.body.replaceChildren(Object.assign(document.createElement('img'), {src: URL.createObjectURL(await (await fetch('http://www.vulnweb.com/acunetix-logo.png')).blob())}))
But you need host permission for vulnweb domain.
Alternative approach could be to use this Firefox exclusive API:
To gain access to the image element - and then:
- either try to fetch it from there (from content script)
- if you can’t fetch it, create canvas using HtmlImageElement: CanvasRenderingContext2D: drawImage() method - Web APIs | MDN and then export image from canvas
- extract image position using Element: getBoundingClientRect() method - Web APIs | MDN and use captureVisibleTab API to take screenshot of that part (again Firefox exclusive feature) extensionTypes.ImageDetails - Mozilla | MDN
monyxie (monyxie@gmail.com) April 29, 2025, 11:09am 3
fetch() works! Host permission is not a problem because I already need <all_urls> anyway.
I ended up with something that looks like this:
if (url.startsWith("http://")) {
url = await fetch(url)
.then((r) => r.blob())
.then((b) => URL.createObjectURL(b))
}
I did have to add http: to connect-src in the CSP.
I’ve actually explored the alternative approach you suggested before. I guess I could get rid of <all_urls> with that approach, but it comes with its own challenges. Specifically, if I do anything async in the menu handler, the handler lose its status of user action and thus won’t be able to open the popup anymore. I think I’ll stay with the fetch approach for now.
Thank you for your help. I really appreciate it.