javascriptdomwindow.openes6-proxy

JavaScript Proxy not working for opened Window object


I need to Proxy a window object opened with window.open.

So far as I understand it (which is not well), the trivial "handler" here should effectively be the identity operation, passing thru property accesses unmodified:

let origOpen = window.open;
window.open = function(url) {
  let openedWindow = origOpen.apply(this, arguments);
  let handler = {
    get(target, prop, receiver) {
      return Reflect.get(...arguments);
    }
  };
  let wrappedWindow = new Proxy(openedWindow, handler);
  return wrappedWindow;
};

let wi = window.open("https://example.net/");
console.log(wi.closed);

However, when this script reaches the line which tries to log wi.closed, it throws the following exception:

Uncaught TypeError: 'get closed' called on an object that does not implement interface Window.

(Using Firefox ESR 115.)


Solution

  • Unfortunately native objects like Window, Event, RegExp, etc. in general don't support calling their methods and getters/setters on their proxied versions, you should call them yourself with the original object.

    Here the getter closed belongs to the window, in other cases it could belong to an instance's prototype (to any prototype in the prototype chain). So the logic of finding a getter could be more complex.

    For methods you could either return a proxy of a method with the apply trap where you call the method with the proper this or like in my example just bind this.

    The same should be applied for setters.

    Note that a returned value from a getter could be a function too, so you should bind it also. Thus the whole logic to cover all cases in generic manner could be quite complicated.

    
        let origOpen = window.open;
        window.open = function(url) {
          let openedWindow = origOpen.apply(this, arguments);
          let handler = {
            get(target, prop){    
              // bind methods to the original object
              if(typeof target[prop] === 'function') return target[prop].bind(target);
              // call getters with the original object
              const desc = Object.getOwnPropertyDescriptor(target, prop);
              if(desc?.get) return desc.get.call(target);
    
              return Reflect.get(...arguments);
            }
          };
          return new Proxy(openedWindow, handler);
        };
    
        let wi = window.open("https://example.net/");
        console.log(wi.closed);