今天写一个插件的时候,页面中有个提交需要用到一个原始的window._token_的变量,我需要使用它,但Chrome插件的Content Scripts的执行环境和原始网页是不一样的,导致没法用window._token_来直接获取。
如果是在Firefox中,我可以使用unsafeWindow._token_来直接获取;很早以前的Chrome插件也是和原始网页共享执行环境的,可惜美好的日子已经一去不复返了。
幸运的是我找到“How can I mimic Greasemonkey/Firefox's unsafeWindow functionality in Chrome?”这个问题,得到了3种解决办法。
现在我要获取它,就可以创建一个script元素,append到head。而这个script元素的执行环境是原始网页的,可以自由使用token这个变量。
不过token取出来后也没法直接传递回content script。好在文档里说DOM是共用的,所以可以在这个script里,把值设置为某个元素的属性或innerText,再在content script里获取就行了:
setTimeout(function() { var script = document.createElement('script'); script.type = 'text/javascript'; script.innerHTML = "document.body.setAttribute('data-token', token);"; document.head.appendChild(script); document.head.removeChild(script); console.log(document.body.getAttribute('data-token')); }, 1000);
另一种办法就是使用location.href,它是用来跳转网页的,但是也可以用来执行JavaScript,并且执行环境也是原始网页:
setTimeout(function() { location.href = "javascript:document.body.setAttribute('data-token', token);"; setTimeout(function() { console.log(document.body.getAttribute('data-token')); }, 0); }, 1000);
这里嵌套使用了setTimeout,原因是跳转是一个事件,并不会打断当前脚本的执行(而添加script元素会立刻执行),因此需要让后面的语句等待事件处理完毕。
不过前面所说的方法都必须先保存,再取出,能这样做的只有字符串等简单类型,对函数则无能为力了。
实际上还有更方便的方法,那就是在DOM上绑定一个事件,而事件的处理函数则返回window变量,再在程序中触发这个事件,就能获得执行环境的window变量了。幸运的是,这个执行环境仍然是原始网页的:
setTimeout(function() { var div = document.createElement('div'); div.setAttribute('onclick', 'return window;'); var unsafeWindow = div.click(); console.log(unsafeWindow.token); }, 1000);