Unfortunately, my problem is somewhat more involved than that. I am able to export functions, but:
JavaScript functions can have two internal methods:
- [[Call]], which is used if the function is called without
new
- [[Construct]], which is used if the function is called with
new
.
Each object which is typeof === 'function'
has either or both of these.
If you attempt to call a function the wrong way, a TypeError
will be thrown.
The problem is that the .message
of this TypeError
contains information that is not available to the script, like the local identifier of the function being called; which means it is not sufficient to check the value of new.target
within the function. It is not possible to throw the correct error here.
So if, for example, I want to expose a method (which mustn’t have a [[Construct]] internal method), I can’t just export a plain function. Calling that with new
can’t produce the correct Error.
Unfortunately it seems to me that Cu.exportFunction
always produces functions with both [[Call]] and [[Construct]] internal methods, and the methods that don’t exist on the original function will then throw. But at that point it’s already to late; the error is not authentic.
The difference between an actuall function missing either of the internal methods and a wrapped function is quite small, but at least the presence of the [[Construct]] method can be reliably checked. For example with this function:
/**
* Tests whether a function can be used as a constructor, without attempting to call that function.
* @param {function} func Function object to test.
* @return {Boolean} True iff func has a [[Construct]] internal method.
* That is, if it returns false, then func is not a function or constructing it with new would throw 'TypeError: <local id> is not a constructor'.
* If it returns true, it may still throw 'TypeError: Illegal constructor.', but is is a constructor.
*/
function isConstructable(func) {
try {
construct(Ctor, [ ], func);
return true;
} catch (_) {
return false;
}
};
class Ctor { }
const { construct, } = Reflect;
So, as I said before, as far as I know, only contentWindow.eval(...)
produces correct functions. But to get that to work on every page I’d have to intercept the CSP, allow 'unsafe-eval'
(which opens a security hole) and then try to stuff that hole again by wrapping eval
, Function
, setTimeout
and everything else covered by that directive. And that’s quite a lot of effort and also quite a mess.
I hoped Firefox would provide a better way o do this.