我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情
大家好,我是心锁,一枚23届准毕业生。
近期在做一个Chrome浏览器截图插件,功能是从浏览器截图并发送图片到企微,便于在远程办公环境下快速从浏览器发送图片进行showCase(目前未真正使用上,原因是截图时html2canvas有错位)
在开发浏览器插件时,有时候会遇到并不需要全屏截图的情况。比如,截图的时候忽略侧边栏只截图右侧的内容区。
所以在这个简单的需求下,我尝试通过允许用户自定义hook来实现
我们要完成hook,本质上就是两种方法。
一种是动态代码注入(文件注入),一种是eval执行字符串
开始之前,我们首先先获取一些(踩坑后明白的)已知限制:
Chrome v3版本全面禁止eval, new Function,不管在background
,inject
还是popup
中都无法通过任何手段开启
Chrome v3版本全面禁止通过script
标签加载外部源文件
Chrome目前上的的插件仅支持v3,v2已经禁上架
那么在此基础上,我们能评估出哪些方案呢?
经过chrome文档的查阅,发现在v3版本中允许对wasm文件做动态导入的操作
…
但是,WebAssembly的难度一下子击中我的心巴
…
不必多说,且往下看
在查阅API文档时发现,chrome支持程序注入,可以胜任动态注入脚本
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ['content-script.js']
});
});
那么我们现在需要考虑的就是能否生成文件并注入。
这就需要两个条件:
File
对象存在对于前者是没有问题的
但是很遗憾,对于将文件转换成地址用于注入常用的APIURL.createObjectURL
是不支持的
结合2.3.2,我们可以获得文件内容~
既然background不支持获得URL,那么我们在inject中跑行不行呢?
确实,我们可以拿到URL
传递URL到background之后再插入到content中
很遗憾还是不行,会直接找不到文件
而另一个方案是在content中注入script,这是实践内容,答案是不行。
否决了三个方案,最后回到了通过eval
的方式解决问题的方法。
上文说过,Chrome全面禁止了eval,但是这并非绝对的。
我们知道,JS作为一款解释性语言,是无法从根源上切断JS的运行的。
Chrome固然禁止了我们使用系统携带的eval解释器,但是并不妨碍我们引入自己的解释器。
一番搜索,找到了曾经超diao的eval5
根据eval5的README,我们完全可以在浏览器沙盒环境中使用eval5
所以,最终方案选择eval5
上边的截图也看到了,eval5目前只支持ES5,同时从作者的回复来看,并没有计划支持ES6
但是如果只是这样多没意思,不会有人还在使用ES5开发吧~
所以我们尝试让eval5支持ES6+
ES5和ES6有哪些区别?
其实我们平时使用的箭头函数、const、let全都是ES6+的内容,习惯了现代JavaScript再去支持ES5编程会感觉非常别扭。
而这也造就了,如果我们要支持ES6,去动手修改eval5的解释器工作量会很大。
所以转变思路,我们把ES6+代码转换成ES5
那自然就引出了老朋友babel
,我们来到babel官网
我们直接揭露谜底,我们本次需要用到的是@babel/standalone
集成包
所以现在,我们可以通过简单的两行代码将ES6+的代码字符串转为ES5字符串
var input = 'const getMessage = () => "Hello World";';
var output = Babel.transform(input, { presets: ["env"] }).code;
在这个基础上,我们尝试一下将Babel与eval5搭配使用。
const { Interpreter } = eval5;
Interpreter.global = window;
const interpreter = new Interpreter();
const baseCode="const getMessage=()=>'Hello World';console.log(getMessage());"
const newCode=Babel.transform(baseCode, { presets: ["env"] }).code
console.log('newCode',newCode)
interpreter.evaluate(newCode);
很显然,一切OK~
现在我们可以愉快地在Chrome v3中使用ES6甚至TS语法了
文章结尾,是我把相关的内容抽离成了一个只有18行代码的github仓库,对于懒人朋友们,只需要将打包产物umd.min.js
在content_scripts中配置好进行注入,即可在Chrome v3插件中使用eval。
除此之外,我们对于使用Babel将ES6+转换成ES5+代码的方案还是要给出一些已知的问题:
Proxy
、Reflect
这些无法被babel兼容的ES6代码,所以我们无法使用这两个特性