I used a solution proposed here.
It is implemented by creating a special protocol that is handled by your Firefox addon, which in turn requests the resources from its folder.
Note that in case the resource folder may contain something non-public then I would add additional checks to allow only these resources that really are intended to be web-accessible.
The custom-protocol code attached to the post mentioned above is available here:
/*
Makes any file within the data directory available to use in an iframe.
Replace this: require("sdk/self").data.url(...)
With this: require("name-of-this-file").url(...)
*/
var { Class } = require('sdk/core/heritage');
var { Unknown, Factory } = require('sdk/platform/xpcom');
var { Cc, Ci, Cr } = require('chrome');
var self = require("sdk/self");
var resourceProtocolHandler = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService)
.getProtocolHandler('resource');
var scheme = "res-" + self.id.toLowerCase().replace(/[^a-z0-9+\-\.]/g, "-");
var AddonProtocolHandler = Class({
extends: Unknown,
interfaces: ['nsIProtocolHandler'],
scheme: scheme,
defaultPort: -1,
protocolFlags: Ci.nsIProtocolHandler.URI_STD
| Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE
| Ci.nsIProtocolHandler.URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT,
newURI: function(spec, originCharset, baseURI) {
let uri = Cc["@mozilla.org/network/standard-url;1"].createInstance(Ci.nsIStandardURL);
uri.init(uri.URLTYPE_STANDARD, this.defaultPort, spec, originCharset, baseURI);
return uri.QueryInterface(Ci.nsIURI);
},
newChannel: function(uri) {
if (uri.spec.indexOf(exports.url("")) != 0) {
throw Cr.NS_ERROR_ILLEGAL_VALUE;
}
var resourceUri = resourceProtocolHandler.newURI(uri.spec.replace(scheme + "://", "resource://"), uri.originCharset, null);
var channel = resourceProtocolHandler.newChannel(resourceUri);
channel.originalURI = uri;
return channel;
},
allowPort: (port, scheme) => false
});
Factory({
contract: "@mozilla.org/network/protocol;1?name=" + scheme,
Component: AddonProtocolHandler
});
exports.url = function(url) {
return self.data.url(url).replace("resource://", scheme + "://");
};
Additional note: for page
scripts (not content
scripts) this custom protocol helps in loading an iframe
and other HTML elements, but not in loading a XMLHttpRequest
or Worker
. For the latter, still cross-origin restrictions apply and a security error is raised: "Access to restricted URI denied" in case of XMLHttpRequest
, and "The operation is insecure." for Worker
.
In contrast, under Chrome the XMLHttpRequest
to web_accessible_resources
is permitted. Worker
does not implement access to web_accessible_resources
under Chrome either.
BUT for Workers
you can use this custom-protocol url for importScripts
method. Using that you can also work around the problem of loading custom resources into Workers
. This code works also in Chrome, as an alternative to using XMLHttpRequest
.
var code = "self.onmessage = function (message)\
{\
self.onmessage = null;\
self.importScripts(message.data);\
};";
var blob = new Blob([code], {type: 'application/javascript'});
var blobUrl = URL.createObjectURL(blob);
var w = new Worker(blobUrl);
w.postMessage(*webAccessibleResourceUrl*);
URL.revokeObjectURL(blobUrl);