In a project, I have to load 3rd party websites in WKWebView. I use iOS recommended userscript to do that. I need to tap on src set to new Image().src and change it if required, without even informing the loaded websites. I had faked Image with a custom class with Proxy object. It faked nicely. But it is a hack and I do not have confidence in it that it will work forever. I saw it fails while doing appendChild(). The idea that I tried is:
const imgProxy = new Proxy(Image, {
set:(a,b,c,d) => {
console.log(`target: ${a} src: ${String(c)}`);
return Reflect.set(a,b,c,d);
}
});
const img = new imgProxy(150,150);
img.src = 'https://via.placeholder.com/150';
document.body.appendChild(img);
It works. But it does not run into the trap method, which is not my intention. Then I tried with new Proxy(new Image(150,150), ...). This falls in trap, but does not work for appendChild() as appendChild rejects not knowing an object other than Node. No where in internet, people had really discussed whether should we use Proxy object to override native behaviour. Idea to do so, sounds fishy. So, I wander what exactly should I do to tap on Image src and to change it for websites that I do not control. Also, I think I do not understand the right intention of Proxy object. Any help is a breeze.
The whole code I used :
const NativeImage = Image;
class CustomImage {
constructor(width, height) {
const nativeImage = new NativeImage(width, height);
const handler = {
set: (target, prop, value) => {
if (prop === 'src') {
value = modifyAsPerMyNeed(value) || value;
}
return (nativeImage)[prop] = value;
},
get: (target, prop) => {
let result = target[prop];
if (typeof result === 'function') {
result = result.bind(target);
}
return result;
}
};
const proxy = new Proxy(Image, handler);
try {
proxy[Symbol.toStringTag] = 'HTMLImageElement';
} catch (e) {
console.log(`HSIWKUserScript error : ${e}`);
}
CustomImage.prototype[Symbol.toStringTag] = NativeImage.prototype.toString();
return proxy;
}
}
if ('toSource' in NativeImage) { // FF extra
Object.defineProperty(CustomImage, 'toSource', {
enumerable: false,
configurable: false,
writable: true,
value: () => {
return (NativeImage as any).toSource();
}
});
}
Object.defineProperty(CustomImage, 'name', {
enumerable: false,
configurable: false,
writable: false,
value: 'Image'
});
Object.defineProperty(CustomImage, 'toString', {
enumerable: true,
configurable: false,
writable: true,
value: () => {
return NativeImage.toString();
}
});
window.Image = CustomImage;