在我当前的项目中,我试图隔离第三方js,但仍允许它进行dom访问,例如,如果js代码使用“ document.cookie
”,它将得到一个空字符串,但如果它将使用“ document.getElementById
”,它将从dom中获得一个真实的节点。
我不能使用常规的iframe(具有sandbox属性或其他属性)来隔离代码,因为正如我提到的那样,我需要允许dom访问。
我目前正在做的是创建一个空白的iframe并挂接其所有的window / document方法/属性,而不是注入第三方js,但是目前我正面临两个问题:
1。window对象包含一些我无法钩住的只读属性,例如window.top
,因此,如果js代码调用“ window.top
”,它将获得最高的访问权限窗口而不是iframe窗口,我可以通过将所有内容包装在iframe的window对象中来解决此问题,
//source: https://blog.javascripting.com/2014/05/19/wrapping-the-dom-window-object/
var wrapper = {};
for (var prop in window) {
(function(prop) {
if (typeof(window[prop]) === 'function') {
wrapper[prop] = function() { return window[prop].apply(window, arguments); }
}
else {
Object.defineProperty(wrapper, prop, {
'get': function() {
if (window[prop] === window) {
return wrapper;
}
else {
return window[prop];
}
},
'set': function(value) { wrapper[prop] = value; }
});
})(prop);
}
,并且在某些属性被引用而没有“窗口”的情况下。前缀我可以使用with语句,所以现在我有了类似的东西:
(function(window) {
with (window) {
//Some third-party js
}
})(wrapper);
但是为整个窗口对象创建一个包装器来处理只读属性似乎没有必要,我只能使用“ with”语句,但是如果调用了“ window.top”没用,那你觉得呢?我上面提到的方法有更好的选择吗?
在某些情况下,主窗口对象上的某些js代码将调用由我们在iframe中运行的第三方js创建的全局属性,因此,创建的属性将存在于iframe窗口对象上,而不存在在顶部窗口对象上,例如,如果我们的第三方js将执行以下操作:
window.SomeProp = {“非常”:function(){}; ,“有用”:function(){},“ prop”:function(){}}
我们在顶部窗口中的代码将无法使用此属性,如果我提前知道属性名称,则可以将其包装并“代理”对iframe属性的调用,但这是针对特定情况的,我正在寻找对于一个通用属性,那么您认为有一种方法可以观察新属性的创建吗?
答案 0 :(得分:0)
我批评你,然后我遇到了类似的问题。因此,我对您提供的此博客中的代码进行了一些升级:
var FakeWindow = /** @class */ (function () {
function FakeWindow() {
return this.prepare();
}
FakeWindow.prototype.prepare = function () {
var wrapper = {};
for (var _i = 0, _a = Object.getOwnPropertyNames(window).sort(); _i < _a.length; _i++) {
var prop = _a[_i];
(function (prop) {
if (typeof (window[prop]) === 'function') {
wrapper[prop] = function () { return window[prop].apply(window, arguments); };
}
else {
Object.defineProperty(wrapper, prop, {
'get': function () {
if (window[prop] === window) {
return wrapper;
}
else {
return window[prop];
}
},
'set': function (value) { wrapper[prop] = value; }
});
}
})(prop);
}
return wrapper;
};
return FakeWindow;
}());
请注意,现在改为Object.getOwnPropertyNames(window)
代替了prop in window
,因为使用prop in window
会丢失许多道具:
let count = 0;
for(prop in window){count++} //count is 270
我的方式:
Object.getOwnPropertyNames(window).length //846
道具数量可能会因浏览器和有效插件的不同而有所不同 因此,基本上将第三方库包装在一些假窗口对象中,就像这样:
const F = new FakeWindow();
(function{const window = F;return /* here goes library content */}).bind(F)()
您可能需要将其包装在eval()
中,以在将其绑定到FakeWindow
之前不执行lib的内容。
对于某些库,只需要不带const window...
的绑定即可。
在ThreeJS和jQuery上进行了测试。