Javascript 更改Window.prototype.open的方式不是';t可检测/可逆

Javascript 更改Window.prototype.open的方式不是';t可检测/可逆,javascript,firefox,firefox-addon,firefox4,prototype-programming,Javascript,Firefox,Firefox Addon,Firefox4,Prototype Programming,我正在寻找从扩展扩展扩展Firefox弹出窗口阻止的方法。一个选项是用包装函数替换网页中的window.open()(或者更确切地说是window.prototype.open())。一个重要的要求是网页无法检测或还原此操作。例如,如果我只是这样做: Window.prototype.open = wrapper; 该网页可以通过执行以下操作轻松还原更改: delete Window.prototype.open; 相反,我可以使用设置高级属性标志: Object.definePropert

我正在寻找从扩展扩展扩展Firefox弹出窗口阻止的方法。一个选项是用包装函数替换网页中的
window.open()
(或者更确切地说是
window.prototype.open()
)。一个重要的要求是网页无法检测或还原此操作。例如,如果我只是这样做:

Window.prototype.open = wrapper;
该网页可以通过执行以下操作轻松还原更改:

delete Window.prototype.open;
相反,我可以使用设置高级属性标志:

Object.defineProperty(Window.prototype, "open", {value: wrapper, configurable: false});
网页无法再还原此更改,但仍能检测到:
删除窗口。原型。打开
通常会更改
窗口的值。原型。打开
(似乎是同一功能的不同实例),此处的
删除
将完全无效。另外,
Window.prototype.open=“test”;删除Window.prototype.open将产生不一致的结果(不同的结果取决于是否为属性指定了
writable:false
标志)


我还可以做些什么来模拟原始属性的行为(除了使用本身有太多问题的二进制XPCOM组件)?

Web浏览器故意阻止这种行为,它用于维护web的安全性,例如,当您使用iFrame时,您不希望该iFrame弄乱或破坏您的页面

但是,与其操纵窗口对象属性,为什么不为窗口对象创建一个包装器,并在本地由包装器覆盖窗口呢

例如:

// Copy window object to wraper
var wrapper = {};
for(prop in window) {
  wrapper[prop] = window[prop];
}

wrapper.open = function yourNewOpenFunction() {
  /// do your custom code here
}

(function fakeScope(window){
    window.open(); // this is wrapper.open
}(wrapper));
顺便说一句,这只影响fakeScope()函数内部的主体,不能全局应用。

您可以尝试使用该接口注册自己的窗口创建者()。这样,您就可以控制是否打开一个新窗口,而不会影响窗口对象本身(从而对网站保持不可见)


我不确定在不被检测到的情况下无法更改
window.open()
的实现是否是一个bug。也许这并不是像
Object.defineProperty
这样的方法的重要要求。但是,也许有必要提交一个bug,看看其他人对将来将此作为一个选项有何想法。毕竟,广告拦截是一个主要的用例。

今天早上我突然想到:你可以使用
Object.freeze(Window.prototype)!
测试表明,用这种大炮保护的方法可以被删除,但它们很容易被检测到


旧答案:

那怎么办?


当然,它们是不稳定的,但它们在Firefox4+中工作,而你不是那种害怕困难的人;)

最后,我不得不放弃使用JavaScript代理来完成这项工作。尽管经过一些努力,我可以为
window.open()
创建一个与原始函数行为完全相同的包装器(需要考虑),但似乎没有合适的方法来替换原始的
window.open()
函数。更改的属性将始终与原始属性的行为不同,这太糟糕了

因此,我决定采用一种不同的方法作为弹出式阻塞解决方案:倾听并查看主题(新窗口)及其开启者。带有非空开启器的窗口是某种弹出窗口。您可以查看URL并决定是否应阻止弹出窗口。若要阻止,将调用
window.stop()
(在发送任何网络请求之前停止所有网络活动)和
window.close()
。后者必须异步调用(有延迟),因为在初始化窗口的过程中,它会导致崩溃。关于这种方法的一些注意事项:

  • 对于在新窗口中打开的弹出窗口,窗口仍将显示,但会立即消失。这似乎是不可避免的
  • 对于网页来说,它看起来像是打开了弹出窗口,但立即关闭了——这不是内置弹出窗口阻止程序的工作方式,更像是外部弹出窗口阻止应用程序
  • 新窗口总是在更改到其实际目标之前先加载
    about:blank
    。对于同源弹出窗口,后者不会发送新的
    内容文档全局创建的
    通知,这是不幸的

总而言之:不完美但可用。而且很简单,,远远超过JavaScript代理所需的代码量。

为什么它必须是不可检测的?@cwallenpoole:网页不应该知道除了常用的弹出窗口拦截器外,还有其他东西在使用-一些网站管理员如果有机会,往往会使用一些非常不友好的技术。你是否排除使用二进制XPCOM组件?@Matthew:不,使用二进制XPCOM组件肯定行不通(请参阅)。该框架不会弄乱页面,因为同源策略会阻止它。无法复制“内置”属性的行为是一个bug,而不是一个特性。至于更换
窗口
对象-即使在这里有帮助,也不可能
(function(){alert(this)})(
将始终显示“真实”全局对象,无论您将
窗口
变量设置为什么。@Omar该循环中
i
从何而来?@WladimirPalant我在上面说过这一点。当然,窗口不能更改,除非您始终控制函数的本地范围。这只是一个快速而肮脏的解决方案,可以避免在将现有代码放入
fakeScope
函数体时搜索/替换代码。
nsIWindowWatcher.setWindowCreator()
看起来是一个可行的解决方案。唯一明显的问题是,获取旧的窗口创建者是不可能的,因此新的窗口创建者必须调用默认的窗口创建者-不可能进行链接。但是这应该很容易用错误报告来修复。是的,我认为setWindowCreator()应该返回旧的窗口创建者(或者他们可以简单地添加一个新的getWindowCreator()