Native Image object proxy in javascript

Viewed 287

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;

1 Answers

I have been so stupid all day yesterday. What I used is MutationObserver and it is working fine. This is far more superior than having any need to intercept javascript objects for DOM interception. Image object ultimately inserts img tag in DOM. I could change src as soon as it is inserted to DOM. But I unnecessarily wrote a wrapper of Image. Removing that wrapper would work perfectly fine, yet satisfy our intention to intercept and modify src attribute.

Related