Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing Window.prototype.open in a way that isn't detectable/reversible

I am looking into ways to extend Firefox pop-up blocking from an extension. One option is replacing window.open() (or rather Window.prototype.open()) in the webpage by a wrapper function. An important requirement is that this manipulation cannot be detected or reverted by the webpage. For example, if I simply do this:

Window.prototype.open = wrapper;

The webpage can easily revert the change by doing:

delete Window.prototype.open;

Instead I can use Object.defineProperty() to set advanced property flags:

Object.defineProperty(Window.prototype, "open", {value: wrapper, configurable: false});

The webpage can no longer revert this change but it can still detect it: delete Window.prototype.open normally changes the value of Window.prototype.open (different instance of the same function it seems), here delete won't have any effect at all. Also, Window.prototype.open = "test";delete Window.prototype.open; will produce inconsistent results (different ones depending on whether writable: false flag is specified for the property).

Is there anything else that I can do to emulate the behavior of the original property (short of using binary XPCOM components which has way too many issues of its own)?

like image 878
Wladimir Palant Avatar asked Jul 11 '11 11:07

Wladimir Palant


2 Answers

You might try using the nsIWindowWatcher interface to register your own window creator (nsIWindowCreator). That way you can control whether a new window is opened without affecting the window object itself (and thus remaining invisible to web sites).

I'm not sure whether the inability to change the implementation of window.open() without this being detectable is a bug. Perhaps it's just not considered an important requirement for methods like Object.defineProperty. But it might be worth filing a bug to see what others think about making this an option in the future. After all, ad blocking is a major use case.

like image 51
Matthew Gertner Avatar answered Oct 19 '22 05:10

Matthew Gertner


In the end I had to give up on using JavaScript proxies for the job. Even though with some effort I can create a wrapper for window.open() that behaves exactly like the original (bug 650299 needs to be considered), there doesn't seem to be a proper way to replace the original window.open() function. The changed property will always behave differently from the original one, too bad.

So I decided to go with a different approach as a pop-up blocking solution: listen for content-document-global-created notification and have a look at the subject (the new window) as well as its opener. Windows with a non-null opener are pop-up windows of some kind. One can look at the URLs and decide whether the pop-up should be blocked. To block one would call window.stop() (stops all network activities before any network requests are sent) and window.close(). The latter has to be called asynchronously (with a delay) because it will cause a crash otherwise as the initialization of the window continues. Some notes on this approach:

  • For pop-ups opening in a new window the window will still show up but disappear immediately. This seems to be unavoidable.
  • For the web page it looks like its pop-up window opened but was closed immediately - this isn't how the built-in pop-up blocker works, more like an external pop-up blocking application.
  • New windows always load about:blank first before changing to their actual destination. For same-origin pop-ups the latter won't send a new content-document-global-created notification which is unfortunate.

All in all: not perfect but usable. And it is very simple, nowhere near the amount of code required for JavaScript proxies.

like image 26
Wladimir Palant Avatar answered Oct 19 '22 04:10

Wladimir Palant