proxifying modifyGetContext
Posted: Mon May 03, 2021 10:53 am
Been looking at this function a while and didn't realize this https://github.com/hackademix/nscl/comm ... 7946672329. Sigh. Oh well, never did say I understand regex
Anyhow... Lets tackle some error messages.
Btw, can't move the call to getContext to the front because if we return null we can't have the canvas initialized to webgl as it still needs to be able to provide other contexts like 2d. i.e.
So to achive this, getCanvas should not be called when the type is webgl. We just must make best effort to not return null when there should be an error.
Different types of errors I have encountered:
The first one should be handled by checking that the object is instance of HTMLCanvasElement. Using proxy will take care of the two others.
webglHook.js:
The need to use a proxy stems mainly from the error message thrown by new operator. For reason unknown, if used on exported getContext, it seems the error gets initialized in contentscript context and while propagating back to the page, causes another error (DOMException).
Generating the error in the exported function for the right scope works around this, but there is no way to make the error message completely correct as the message should contain the line that is executed and this info is not available in the exported function. e.g.
So proxy seems must. Note that the proxy is created in the page scope. Otherwise the error message from "new" will still be initialized in the contentscript context. Only the proxy handler is exported.
Also HTMLCanvasElement.prototype.getContext.length is now 1 as it should be.
Oh! Does line "let target = canvas === "HTMLCanvasElement" && document.contains(thisArg) ? thisArg : scope;" have some planned use? postMessage does not appear to use the target for anything.
Anyhow... Lets tackle some error messages.
Btw, can't move the call to getContext to the front because if we return null we can't have the canvas initialized to webgl as it still needs to be able to provide other contexts like 2d. i.e.
Code: Select all
// Native behaviour. Assume webgl disabled from about:config. We perform these two consecutive calls:
canvas.getContext("webgl") -> null
canvas.getContext("2d") -> 2d context
// Assume webgl enabled.
canvas.getContext("webgl") -> webgl context
canvas.getContext("2d") -> null
Different types of errors I have encountered:
Code: Select all
TypeError: 'getContext' called on an object that does not implement interface HTMLCanvasElement. HTMLCanvasElement.prototype.getContext()
TypeError: HTMLCanvasElement.getContext: At least 1 argument required, but only 0 passed canvas.getContext()
TypeError: getContext is not a constructor new canvas.getContext
webglHook.js:
Code: Select all
function modifyGetContext(scope, env) {
let dispatchEvent = EventTarget.prototype.dispatchEvent;
let { Event } = scope;
for (let canvas of ["HTMLCanvasElement", "OffscreenCanvas"]) {
if (!(canvas in scope)) continue;
const getContext = scope[canvas].prototype.getContext;
const handler = cloneInto({
apply: function(targetObj, thisArg, argumentsList) {
if (thisArg instanceof HTMLCanvasElement && /webgl/i.test(argumentsList[0])) {
let target = canvas === "HTMLCanvasElement" && document.contains(thisArg) ? thisArg : scope;
env.port.postMessage("webgl", target);
return null;
}
return getContext.call(thisArg, ...argumentsList);
}
}, scope, {cloneFunctions: true});
const proxy = new scope.Proxy(getContext, handler);
scope[canvas].prototype.getContext = proxy;
}
}
Generating the error in the exported function for the right scope works around this, but there is no way to make the error message completely correct as the message should contain the line that is executed and this info is not available in the exported function. e.g.
Code: Select all
new canvas.getContext -> TypeError: canvas.getContext is not a constructor
new windows.canvas.getContext -> TypeError: windows.canvas.getContext is not a constructor
Also HTMLCanvasElement.prototype.getContext.length is now 1 as it should be.
Oh! Does line "let target = canvas === "HTMLCanvasElement" && document.contains(thisArg) ? thisArg : scope;" have some planned use? postMessage does not appear to use the target for anything.